@zvec/zvec-grep 0.1.5 → 0.1.6

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 (317) hide show
  1. package/README.md +88 -249
  2. package/README_CN.md +82 -246
  3. package/dist/authorization/index.d.ts +7 -0
  4. package/dist/authorization/index.js +7 -0
  5. package/dist/authorization/index.js.map +1 -0
  6. package/dist/authorization/manager.d.ts +8 -0
  7. package/dist/authorization/manager.js +19 -0
  8. package/dist/authorization/manager.js.map +1 -0
  9. package/dist/authorization/operation.d.ts +7 -0
  10. package/dist/authorization/operation.js +50 -0
  11. package/dist/authorization/operation.js.map +1 -0
  12. package/dist/authorization/planner.d.ts +21 -0
  13. package/dist/authorization/planner.js +97 -0
  14. package/dist/authorization/planner.js.map +1 -0
  15. package/dist/authorization/prompt.d.ts +11 -0
  16. package/dist/authorization/prompt.js +62 -0
  17. package/dist/authorization/prompt.js.map +1 -0
  18. package/dist/authorization/store.d.ts +20 -0
  19. package/dist/authorization/store.js +251 -0
  20. package/dist/authorization/store.js.map +1 -0
  21. package/dist/authorization/target.d.ts +15 -0
  22. package/dist/authorization/target.js +49 -0
  23. package/dist/authorization/target.js.map +1 -0
  24. package/dist/authorization/types.d.ts +61 -0
  25. package/dist/authorization/types.js +2 -0
  26. package/dist/authorization/types.js.map +1 -0
  27. package/dist/cli/args.d.ts +1 -1
  28. package/dist/cli/args.js +304 -54
  29. package/dist/cli/args.js.map +1 -1
  30. package/dist/cli/auth.d.ts +21 -0
  31. package/dist/cli/auth.js +212 -0
  32. package/dist/cli/auth.js.map +1 -0
  33. package/dist/cli/commands.js +433 -618
  34. package/dist/cli/commands.js.map +1 -1
  35. package/dist/cli/errors.js +2 -2
  36. package/dist/cli/errors.js.map +1 -1
  37. package/dist/cli/format/context.d.ts +1 -0
  38. package/dist/cli/format/context.js +241 -45
  39. package/dist/cli/format/context.js.map +1 -1
  40. package/dist/cli/format/debug.js +2 -2
  41. package/dist/cli/format/debug.js.map +1 -1
  42. package/dist/cli/format/progress.d.ts +4 -0
  43. package/dist/cli/format/progress.js +219 -46
  44. package/dist/cli/format/progress.js.map +1 -1
  45. package/dist/cli/format/status.d.ts +77 -6
  46. package/dist/cli/format/status.js +302 -106
  47. package/dist/cli/format/status.js.map +1 -1
  48. package/dist/cli/help.js +258 -59
  49. package/dist/cli/help.js.map +1 -1
  50. package/dist/cli/index.js +0 -0
  51. package/dist/cli/install.d.ts +4 -0
  52. package/dist/cli/install.js +845 -0
  53. package/dist/cli/install.js.map +1 -0
  54. package/dist/cli/managed-rg.d.ts +10 -0
  55. package/dist/cli/managed-rg.js +329 -0
  56. package/dist/cli/managed-rg.js.map +1 -0
  57. package/dist/cli/types.d.ts +20 -6
  58. package/dist/cli/types.js.map +1 -1
  59. package/dist/client/daemon-client.d.ts +19 -0
  60. package/dist/client/daemon-client.js +180 -0
  61. package/dist/client/daemon-client.js.map +1 -0
  62. package/dist/client/mode-router.d.ts +9 -0
  63. package/dist/client/mode-router.js +28 -0
  64. package/dist/client/mode-router.js.map +1 -0
  65. package/dist/client/search-policy.d.ts +11 -0
  66. package/dist/client/search-policy.js +15 -0
  67. package/dist/client/search-policy.js.map +1 -0
  68. package/dist/daemon/backend.d.ts +76 -0
  69. package/dist/daemon/backend.js +1049 -0
  70. package/dist/daemon/backend.js.map +1 -0
  71. package/dist/daemon/change-set.d.ts +25 -0
  72. package/dist/daemon/change-set.js +101 -0
  73. package/dist/daemon/change-set.js.map +1 -0
  74. package/dist/daemon/config.d.ts +24 -0
  75. package/dist/daemon/config.js +88 -0
  76. package/dist/daemon/config.js.map +1 -0
  77. package/dist/daemon/errors.d.ts +5 -0
  78. package/dist/daemon/errors.js +11 -0
  79. package/dist/daemon/errors.js.map +1 -0
  80. package/dist/daemon/http-server.d.ts +30 -0
  81. package/dist/daemon/http-server.js +287 -0
  82. package/dist/daemon/http-server.js.map +1 -0
  83. package/dist/daemon/index-coordinator.d.ts +23 -0
  84. package/dist/daemon/index-coordinator.js +68 -0
  85. package/dist/daemon/index-coordinator.js.map +1 -0
  86. package/dist/daemon/job-scheduler.d.ts +70 -0
  87. package/dist/daemon/job-scheduler.js +366 -0
  88. package/dist/daemon/job-scheduler.js.map +1 -0
  89. package/dist/daemon/logger.d.ts +9 -0
  90. package/dist/daemon/logger.js +53 -0
  91. package/dist/daemon/logger.js.map +1 -0
  92. package/dist/daemon/model-pool.d.ts +45 -0
  93. package/dist/daemon/model-pool.js +187 -0
  94. package/dist/daemon/model-pool.js.map +1 -0
  95. package/dist/daemon/root-lease.d.ts +14 -0
  96. package/dist/daemon/root-lease.js +188 -0
  97. package/dist/daemon/root-lease.js.map +1 -0
  98. package/dist/daemon/root-runtime.d.ts +78 -0
  99. package/dist/daemon/root-runtime.js +333 -0
  100. package/dist/daemon/root-runtime.js.map +1 -0
  101. package/dist/daemon/runtime-manager.d.ts +45 -0
  102. package/dist/daemon/runtime-manager.js +243 -0
  103. package/dist/daemon/runtime-manager.js.map +1 -0
  104. package/dist/daemon/runtime.d.ts +12 -0
  105. package/dist/daemon/runtime.js +102 -0
  106. package/dist/daemon/runtime.js.map +1 -0
  107. package/dist/daemon/server-controller.d.ts +39 -0
  108. package/dist/daemon/server-controller.js +203 -0
  109. package/dist/daemon/server-controller.js.map +1 -0
  110. package/dist/daemon/watch-manager.d.ts +50 -0
  111. package/dist/daemon/watch-manager.js +293 -0
  112. package/dist/daemon/watch-manager.js.map +1 -0
  113. package/dist/daemon/workspace-read-session-cache.d.ts +31 -0
  114. package/dist/daemon/workspace-read-session-cache.js +115 -0
  115. package/dist/daemon/workspace-read-session-cache.js.map +1 -0
  116. package/dist/engine/config.d.ts +30 -8
  117. package/dist/engine/config.js +171 -45
  118. package/dist/engine/config.js.map +1 -1
  119. package/dist/engine/{errors/index.d.ts → errors.d.ts} +6 -1
  120. package/dist/engine/{errors/details.js → errors.js} +17 -4
  121. package/dist/engine/errors.js.map +1 -0
  122. package/dist/engine/extraction/code/adapter.d.ts +1 -1
  123. package/dist/engine/extraction/code/extractor.d.ts +5 -11
  124. package/dist/engine/extraction/code/extractor.js +105 -44
  125. package/dist/engine/extraction/code/extractor.js.map +1 -1
  126. package/dist/engine/extraction/code/families/c-family.js +1 -1
  127. package/dist/engine/extraction/code/families/c-family.js.map +1 -1
  128. package/dist/engine/extraction/code/families/js-ts.d.ts +1 -1
  129. package/dist/engine/extraction/code/families/metadata.d.ts +1 -1
  130. package/dist/engine/extraction/code/tree-sitter/grammar.js.map +1 -0
  131. package/dist/engine/extraction/code/tree-sitter/nodes.js.map +1 -0
  132. package/dist/engine/extraction/code/tree-sitter/parser.js.map +1 -0
  133. package/dist/engine/extraction/image/extractor.d.ts +5 -0
  134. package/dist/engine/extraction/image/extractor.js +35 -0
  135. package/dist/engine/extraction/image/extractor.js.map +1 -0
  136. package/dist/engine/extraction/index.d.ts +3 -44
  137. package/dist/engine/extraction/index.js +1 -280
  138. package/dist/engine/extraction/index.js.map +1 -1
  139. package/dist/engine/extraction/markdown/extractor.d.ts +5 -11
  140. package/dist/engine/extraction/markdown/extractor.js +35 -16
  141. package/dist/engine/extraction/markdown/extractor.js.map +1 -1
  142. package/dist/engine/extraction/runtime.d.ts +4 -0
  143. package/dist/engine/extraction/runtime.js +26 -0
  144. package/dist/engine/extraction/runtime.js.map +1 -0
  145. package/dist/engine/extraction/source.d.ts +18 -0
  146. package/dist/engine/extraction/source.js +22 -0
  147. package/dist/engine/extraction/source.js.map +1 -0
  148. package/dist/engine/extraction/text/extractor.d.ts +7 -0
  149. package/dist/engine/extraction/text/extractor.js +168 -0
  150. package/dist/engine/extraction/text/extractor.js.map +1 -0
  151. package/dist/engine/extraction/types.d.ts +4 -0
  152. package/dist/engine/{models → extraction}/types.js.map +1 -1
  153. package/dist/engine/{files/file-type.d.ts → file-type.d.ts} +1 -1
  154. package/dist/engine/file-type.js.map +1 -0
  155. package/dist/engine/index-status.d.ts +9 -0
  156. package/dist/engine/index-status.js +38 -0
  157. package/dist/engine/index-status.js.map +1 -0
  158. package/dist/engine/manifest.d.ts +12 -0
  159. package/dist/engine/manifest.js +120 -0
  160. package/dist/engine/manifest.js.map +1 -0
  161. package/dist/engine/models/{providers/llama-cpp/embedding.d.ts → backends/llama-cpp.d.ts} +25 -20
  162. package/dist/engine/models/{providers/llama-cpp/embedding.js → backends/llama-cpp.js} +127 -106
  163. package/dist/engine/models/backends/llama-cpp.js.map +1 -0
  164. package/dist/engine/models/backends/model2vec.d.ts +57 -0
  165. package/dist/engine/models/backends/model2vec.js +332 -0
  166. package/dist/engine/models/backends/model2vec.js.map +1 -0
  167. package/dist/engine/models/backends/qwen.d.ts +44 -0
  168. package/dist/engine/models/{providers/qwen/embedding.js → backends/qwen.js} +155 -94
  169. package/dist/engine/models/backends/qwen.js.map +1 -0
  170. package/dist/engine/models/backends/transformers-js.d.ts +87 -0
  171. package/dist/engine/models/backends/transformers-js.js +316 -0
  172. package/dist/engine/models/backends/transformers-js.js.map +1 -0
  173. package/dist/engine/models/catalog.d.ts +188 -23
  174. package/dist/engine/models/catalog.js +172 -25
  175. package/dist/engine/models/catalog.js.map +1 -1
  176. package/dist/engine/models/download-progress.d.ts +15 -0
  177. package/dist/engine/models/download-progress.js +48 -0
  178. package/dist/engine/models/download-progress.js.map +1 -0
  179. package/dist/engine/models/embeddings.d.ts +58 -18
  180. package/dist/engine/models/embeddings.js +52 -28
  181. package/dist/engine/models/embeddings.js.map +1 -1
  182. package/dist/engine/models/factory.d.ts +2 -5
  183. package/dist/engine/models/factory.js +41 -56
  184. package/dist/engine/models/factory.js.map +1 -1
  185. package/dist/engine/models/index.d.ts +4 -6
  186. package/dist/engine/models/index.js +4 -4
  187. package/dist/engine/models/index.js.map +1 -1
  188. package/dist/engine/models/ranking.d.ts +7 -3
  189. package/dist/engine/models/resolution.d.ts +8 -0
  190. package/dist/engine/models/resolution.js +22 -0
  191. package/dist/engine/models/resolution.js.map +1 -0
  192. package/dist/engine/pipeline/indexing/index.d.ts +9 -7
  193. package/dist/engine/pipeline/indexing/index.js +360 -121
  194. package/dist/engine/pipeline/indexing/index.js.map +1 -1
  195. package/dist/engine/pipeline/indexing/root-paths.js +6 -6
  196. package/dist/engine/pipeline/indexing/root-paths.js.map +1 -1
  197. package/dist/engine/pipeline/indexing/scanner/index.d.ts +6 -1
  198. package/dist/engine/pipeline/indexing/scanner/index.js +233 -29
  199. package/dist/engine/pipeline/indexing/scanner/index.js.map +1 -1
  200. package/dist/engine/pipeline/search/index.d.ts +6 -6
  201. package/dist/engine/pipeline/search/index.js +8 -10
  202. package/dist/engine/pipeline/search/index.js.map +1 -1
  203. package/dist/engine/service/index.d.ts +3 -2
  204. package/dist/engine/service/index.js +1 -1
  205. package/dist/engine/service/index.js.map +1 -1
  206. package/dist/engine/service/root.d.ts +13 -12
  207. package/dist/engine/service/root.js +35 -24
  208. package/dist/engine/service/root.js.map +1 -1
  209. package/dist/engine/service/structure-enrichment.js +12 -14
  210. package/dist/engine/service/structure-enrichment.js.map +1 -1
  211. package/dist/engine/service/types.d.ts +32 -37
  212. package/dist/engine/service/workspace-index.d.ts +24 -0
  213. package/dist/engine/service/workspace-index.js +151 -0
  214. package/dist/engine/service/workspace-index.js.map +1 -0
  215. package/dist/engine/service/zvec-grep.d.ts +12 -1
  216. package/dist/engine/service/zvec-grep.js +677 -486
  217. package/dist/engine/service/zvec-grep.js.map +1 -1
  218. package/dist/engine/storage/index.d.ts +8 -4
  219. package/dist/engine/storage/index.js +1 -1
  220. package/dist/engine/storage/index.js.map +1 -1
  221. package/dist/engine/storage/zvec.d.ts +10 -33
  222. package/dist/engine/storage/zvec.js +95 -38
  223. package/dist/engine/storage/zvec.js.map +1 -1
  224. package/dist/engine/types.d.ts +15 -23
  225. package/dist/engine/types.js +1 -1
  226. package/dist/engine/types.js.map +1 -1
  227. package/dist/engine/utils/daemon-lease.d.ts +17 -0
  228. package/dist/engine/utils/daemon-lease.js +175 -0
  229. package/dist/engine/utils/daemon-lease.js.map +1 -0
  230. package/dist/engine/utils/file-selection.js +38 -1
  231. package/dist/engine/utils/file-selection.js.map +1 -1
  232. package/dist/engine/utils/lock.js +1 -1
  233. package/dist/engine/utils/lock.js.map +1 -1
  234. package/dist/index-progress.d.ts +10 -0
  235. package/dist/index-progress.js +159 -0
  236. package/dist/index-progress.js.map +1 -0
  237. package/dist/index.d.ts +4 -4
  238. package/dist/index.js +1 -1
  239. package/dist/index.js.map +1 -1
  240. package/dist/mcp/http-transport.d.ts +27 -0
  241. package/dist/mcp/http-transport.js +182 -0
  242. package/dist/mcp/http-transport.js.map +1 -0
  243. package/dist/mcp/input-normalization.d.ts +38 -0
  244. package/dist/mcp/input-normalization.js +95 -0
  245. package/dist/mcp/input-normalization.js.map +1 -0
  246. package/dist/mcp/progress-heartbeat.d.ts +17 -0
  247. package/dist/mcp/progress-heartbeat.js +46 -0
  248. package/dist/mcp/progress-heartbeat.js.map +1 -0
  249. package/dist/mcp/request-metadata.d.ts +2 -0
  250. package/dist/mcp/request-metadata.js +11 -0
  251. package/dist/mcp/request-metadata.js.map +1 -0
  252. package/dist/mcp/request-state.d.ts +40 -0
  253. package/dist/mcp/request-state.js +210 -0
  254. package/dist/mcp/request-state.js.map +1 -0
  255. package/dist/mcp/result-format.d.ts +13 -0
  256. package/dist/mcp/result-format.js +75 -0
  257. package/dist/mcp/result-format.js.map +1 -0
  258. package/dist/mcp/schemas.d.ts +430 -0
  259. package/dist/mcp/schemas.js +447 -0
  260. package/dist/mcp/schemas.js.map +1 -0
  261. package/dist/mcp/tools.d.ts +150 -0
  262. package/dist/mcp/tools.js +448 -0
  263. package/dist/mcp/tools.js.map +1 -0
  264. package/dist/mcp/toolset.d.ts +5 -0
  265. package/dist/mcp/toolset.js +12 -0
  266. package/dist/mcp/toolset.js.map +1 -0
  267. package/dist/observability/trace-context.d.ts +11 -0
  268. package/dist/observability/trace-context.js +138 -0
  269. package/dist/observability/trace-context.js.map +1 -0
  270. package/docs/01-agents.md +111 -0
  271. package/docs/02-cli.md +229 -0
  272. package/docs/03-mcp.md +167 -0
  273. package/docs/04-pipeline.md +218 -0
  274. package/docs/05-architecture.md +86 -0
  275. package/docs/06-server.md +210 -0
  276. package/docs/07-embedding.md +179 -0
  277. package/docs/08-roadmap.md +85 -0
  278. package/docs/README.md +53 -0
  279. package/package.json +17 -8
  280. package/skills/zvec-grep/SKILL.md +43 -133
  281. package/skills/zvec-grep/agents/openai.yaml +2 -2
  282. package/skills/zvec-grep/references/cli-fallback.md +85 -0
  283. package/dist/cli/mcp.d.ts +0 -2
  284. package/dist/cli/mcp.js +0 -354
  285. package/dist/cli/mcp.js.map +0 -1
  286. package/dist/engine/collection/index.d.ts +0 -55
  287. package/dist/engine/collection/index.js +0 -710
  288. package/dist/engine/collection/index.js.map +0 -1
  289. package/dist/engine/errors/details.d.ts +0 -6
  290. package/dist/engine/errors/details.js.map +0 -1
  291. package/dist/engine/errors/index.js +0 -16
  292. package/dist/engine/errors/index.js.map +0 -1
  293. package/dist/engine/extraction/tree-sitter/grammar.js.map +0 -1
  294. package/dist/engine/extraction/tree-sitter/nodes.js.map +0 -1
  295. package/dist/engine/extraction/tree-sitter/parser.js.map +0 -1
  296. package/dist/engine/files/file-type.js.map +0 -1
  297. package/dist/engine/models/providers/index.d.ts +0 -2
  298. package/dist/engine/models/providers/index.js +0 -3
  299. package/dist/engine/models/providers/index.js.map +0 -1
  300. package/dist/engine/models/providers/llama-cpp/embedding.js.map +0 -1
  301. package/dist/engine/models/providers/llama-cpp/index.d.ts +0 -1
  302. package/dist/engine/models/providers/llama-cpp/index.js +0 -2
  303. package/dist/engine/models/providers/llama-cpp/index.js.map +0 -1
  304. package/dist/engine/models/providers/qwen/embedding.d.ts +0 -42
  305. package/dist/engine/models/providers/qwen/embedding.js.map +0 -1
  306. package/dist/engine/models/providers/qwen/index.d.ts +0 -1
  307. package/dist/engine/models/providers/qwen/index.js +0 -2
  308. package/dist/engine/models/providers/qwen/index.js.map +0 -1
  309. package/dist/engine/models/types.d.ts +0 -30
  310. /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/grammar.d.ts +0 -0
  311. /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/grammar.js +0 -0
  312. /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/nodes.d.ts +0 -0
  313. /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/nodes.js +0 -0
  314. /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/parser.d.ts +0 -0
  315. /package/dist/engine/extraction/{tree-sitter → code/tree-sitter}/parser.js +0 -0
  316. /package/dist/engine/{models → extraction}/types.js +0 -0
  317. /package/dist/engine/{files/file-type.js → file-type.js} +0 -0
@@ -0,0 +1,1049 @@
1
+ import { createZvecGrep } from "../engine/service/index.js";
2
+ import { isEngineError } from "../engine/errors.js";
3
+ import { readGlobalConfig, resolveEmbeddingRuntimeOptions, } from "../engine/config.js";
4
+ import { readWorkspaceManifest } from "../engine/manifest.js";
5
+ import { workspaceIndexLocation } from "../engine/service/root.js";
6
+ import { resolveEmbeddingReference } from "../engine/models/index.js";
7
+ import { indexCompletionForJob, indexCompletionFromStatus, } from "../engine/index-status.js";
8
+ import { contextOptionsFromRgInput, normalizePlainStringList, } from "../mcp/input-normalization.js";
9
+ import { DaemonError } from "./errors.js";
10
+ import { JobScheduler, } from "./job-scheduler.js";
11
+ import { EmbeddingModelPool, } from "./model-pool.js";
12
+ import { IndexCoordinator, } from "./index-coordinator.js";
13
+ import { inspectRoot, resolveRequestedRoot, RuntimeManager, } from "./runtime-manager.js";
14
+ import { WatchManager } from "./watch-manager.js";
15
+ import { rootIdentity } from "./logger.js";
16
+ import { RemoteEmbeddingAuthorizationManager, RemoteEmbeddingAuthorizationStore, planRemoteIndexAuthorization, planRemoteSearchAuthorization, withRemoteEmbeddingOperationPermit, } from "../authorization/index.js";
17
+ const DEFAULT_LOCAL_EMBEDDING = "local/embeddinggemma-300m";
18
+ export class DaemonBackend {
19
+ options;
20
+ modelPool;
21
+ runtimeManager;
22
+ scheduler;
23
+ startedAt = Date.now();
24
+ statusCache = new Map();
25
+ workspaceRuntimeCache = new Map();
26
+ watchers = new Map();
27
+ indexCoordinators = new Map();
28
+ droppingRoots = new Set();
29
+ authorizationManager;
30
+ shuttingDown = false;
31
+ closePromise;
32
+ constructor(options) {
33
+ this.options = options;
34
+ this.authorizationManager = new RemoteEmbeddingAuthorizationManager(options.authorizationStore ??
35
+ new RemoteEmbeddingAuthorizationStore({
36
+ signingKeyPath: options.serviceOptions?.authorizationSigningKeyPath,
37
+ }));
38
+ this.modelPool = new EmbeddingModelPool({
39
+ ...options.modelPoolOptions,
40
+ serviceOptions: options.serviceOptions,
41
+ logger: options.logger,
42
+ });
43
+ this.runtimeManager = new RuntimeManager({
44
+ modelPool: this.modelPool,
45
+ serviceOptions: options.serviceOptions,
46
+ readSessionIdleTtlMs: options.readSessionIdleTtlMs,
47
+ runtimeIdleTtlMs: options.runtimeIdleTtlMs,
48
+ onRuntimeEvicted: async (root) => {
49
+ this.statusCache.delete(root);
50
+ this.workspaceRuntimeCache.delete(root);
51
+ await this.closeWatcher(root);
52
+ },
53
+ });
54
+ this.scheduler = new JobScheduler({
55
+ ...options.schedulerOptions,
56
+ logger: options.logger,
57
+ });
58
+ }
59
+ async planIndexAuthorization(input) {
60
+ if (input.drop === true) {
61
+ return undefined;
62
+ }
63
+ const requestedRoot = await resolveRequestedRoot(input.root, false);
64
+ if (input.rebuild !== true && this.scheduler.hasActiveRoot(requestedRoot)) {
65
+ return undefined;
66
+ }
67
+ const info = await inspectRoot(input.root, this.options.serviceOptions, input.rebuild !== true);
68
+ let modelLoadRequest;
69
+ try {
70
+ modelLoadRequest = this.indexModelLoadRequest(info, input);
71
+ }
72
+ catch (error) {
73
+ if (error instanceof DaemonError && error.code === "MODEL_LOAD_FAILED") {
74
+ return undefined;
75
+ }
76
+ throw error;
77
+ }
78
+ let lease;
79
+ try {
80
+ lease = await this.modelPool.acquire(modelLoadRequest);
81
+ }
82
+ catch (error) {
83
+ if (isEngineError(error)) {
84
+ return undefined;
85
+ }
86
+ throw error;
87
+ }
88
+ let schema;
89
+ let modelInfo;
90
+ try {
91
+ schema = embeddingSchema(lease.model);
92
+ modelInfo = lease.model.info;
93
+ }
94
+ finally {
95
+ lease.release();
96
+ }
97
+ const existing = info.workspaceIndex?.embedding;
98
+ if (existing &&
99
+ input.embedding &&
100
+ input.rebuild !== true &&
101
+ (existing.provider !== schema.provider || existing.model !== schema.model)) {
102
+ throw new DaemonError("EMBEDDING_MODEL_MISMATCH", `Existing model ${existing.provider}/${existing.model} does not match requested model ${schema.provider}/${schema.model}; use rebuild to change models.`);
103
+ }
104
+ return await planRemoteIndexAuthorization({
105
+ info,
106
+ model: modelInfo,
107
+ rebuild: input.rebuild,
108
+ store: this.authorizationManager.store,
109
+ });
110
+ }
111
+ async planSearchAuthorization(input) {
112
+ const requestedRoot = await resolveRequestedRoot(input.root, false);
113
+ const activeRuntime = this.runtimeManager.getByCanonicalRoot(requestedRoot);
114
+ if (activeRuntime?.embeddingProvider() &&
115
+ activeRuntime.embeddingProvider() !== "qwen") {
116
+ return undefined;
117
+ }
118
+ const cachedInfo = activeRuntime
119
+ ? this.statusCache.get(requestedRoot)
120
+ : undefined;
121
+ let info;
122
+ if (cachedInfo) {
123
+ info = cachedInfo;
124
+ }
125
+ else {
126
+ try {
127
+ info = await this.inspectRoot(input.root, true);
128
+ this.statusCache.set(requestedRoot, info);
129
+ }
130
+ catch (error) {
131
+ const cached = this.statusCache.get(requestedRoot);
132
+ if (!cached ||
133
+ !isEngineError(error) ||
134
+ error.code !== "ZVEC_GREP.ENGINE.LOCK.BUSY") {
135
+ throw error;
136
+ }
137
+ info = cached;
138
+ }
139
+ }
140
+ const canonicalRoot = await resolveRequestedRoot(info.root, false);
141
+ const schema = info.workspaceIndex?.embedding;
142
+ if (!info.indexed || !schema || schema.provider !== "qwen") {
143
+ return undefined;
144
+ }
145
+ const modelLoadRequest = this.searchModelLoadRequest(info, input);
146
+ const modelInfo = await this.loadEmbeddingModelInfo(modelLoadRequest);
147
+ return await planRemoteSearchAuthorization({
148
+ info,
149
+ model: modelInfo,
150
+ search: input,
151
+ runtimeNeedsReconciliation: this.runtimeManager
152
+ .getByCanonicalRoot(canonicalRoot)
153
+ ?.needsReconciliation() ?? false,
154
+ store: this.authorizationManager.store,
155
+ });
156
+ }
157
+ async existingRemoteEmbeddingPermit(plan) {
158
+ return await this.authorizationManager.existingWorkspacePermit(plan);
159
+ }
160
+ async grantRemoteEmbedding(plan, scope) {
161
+ return await this.authorizationManager.grant(plan, scope);
162
+ }
163
+ async index(input, options = {}) {
164
+ if (input.drop === true) {
165
+ assertDropOnlyInput(input);
166
+ const result = await this.dropIndex(input);
167
+ return {
168
+ root: result.root,
169
+ jobId: "drop",
170
+ state: "succeeded",
171
+ reused: false,
172
+ action: "drop",
173
+ dropped: result.removed,
174
+ };
175
+ }
176
+ const requestedRoot = await resolveRequestedRoot(input.root, true);
177
+ this.assertRootNotDropping(requestedRoot);
178
+ const runtime = await this.runtimeManager.activateForIndex(requestedRoot);
179
+ this.ensureWatcher(runtime);
180
+ const activeJob = this.scheduler.getByRoot(runtime.canonicalRoot);
181
+ const followsNarrowJob = activeJob?.state === "queued" || activeJob?.state === "running"
182
+ ? activeJob.reason === "watch"
183
+ : false;
184
+ const createsWork = !this.scheduler.hasActiveRoot(runtime.canonicalRoot) ||
185
+ input.rebuild === true ||
186
+ followsNarrowJob;
187
+ const targetRevision = createsWork
188
+ ? runtime.markDirty()
189
+ : runtime.snapshot().dirtyRevision;
190
+ runtime.setWriterPending(true);
191
+ let submitted;
192
+ try {
193
+ submitted = this.scheduler.submit({
194
+ canonicalRoot: runtime.canonicalRoot,
195
+ reason: "manual",
196
+ followupIfRunning: input.rebuild === true || followsNarrowJob,
197
+ run: (report, signal) => runtime.withWrite(async () => {
198
+ const proof = await this.runIndex(runtime, input, report, options.authorization, signal);
199
+ if (proof.reconciled) {
200
+ runtime.markReconciled(targetRevision, proof.reconciliationEpoch);
201
+ }
202
+ else {
203
+ runtime.markIndexed(targetRevision);
204
+ }
205
+ }),
206
+ });
207
+ }
208
+ catch (error) {
209
+ runtime.setWriterPending(false);
210
+ throw error;
211
+ }
212
+ if (!submitted.reused) {
213
+ void this.scheduler.waitForRootIdle(runtime.canonicalRoot).finally(() => {
214
+ runtime.setWriterPending(false);
215
+ });
216
+ }
217
+ const job = input.wait
218
+ ? await this.scheduler.wait(submitted.job.id, options.onProgress)
219
+ : submitted.job;
220
+ if (input.wait) {
221
+ await this.settleKnownChanges(runtime);
222
+ runtime.setWriterPending(false);
223
+ }
224
+ this.options.logger?.event("job.submitted", {
225
+ root_id: rootIdentity(runtime.canonicalRoot),
226
+ job_id: job.id,
227
+ reason: "manual",
228
+ state: job.state,
229
+ reused: submitted.reused,
230
+ });
231
+ return {
232
+ root: runtime.canonicalRoot,
233
+ jobId: job.id,
234
+ state: job.state,
235
+ reused: submitted.reused,
236
+ action: "index",
237
+ error: job.error,
238
+ };
239
+ }
240
+ async dropIndex(input) {
241
+ const canonicalRoot = await resolveRequestedRoot(input.root, true);
242
+ if (this.droppingRoots.has(canonicalRoot)) {
243
+ throw new DaemonError("INDEX_DROP_IN_PROGRESS", "The Workspace index is already being dropped.");
244
+ }
245
+ this.droppingRoots.add(canonicalRoot);
246
+ try {
247
+ await this.closeWatcher(canonicalRoot);
248
+ this.scheduler.cancelRoot(canonicalRoot);
249
+ await this.scheduler.waitForRootIdle(canonicalRoot);
250
+ const runtime = this.runtimeManager.getByCanonicalRoot(canonicalRoot);
251
+ const drop = async () => {
252
+ const service = await (this.options.createService ?? createZvecGrep)({
253
+ ...this.options.serviceOptions,
254
+ root: canonicalRoot,
255
+ daemonInstanceToken: this.runtimeManager.instanceToken,
256
+ });
257
+ try {
258
+ return await service.dropIndex({ root: canonicalRoot });
259
+ }
260
+ finally {
261
+ await service.close();
262
+ }
263
+ };
264
+ const removed = runtime ? await runtime.withWrite(drop) : await drop();
265
+ this.options.logger?.event("index.dropped", {
266
+ root_id: rootIdentity(canonicalRoot),
267
+ dropped: removed,
268
+ });
269
+ return { root: canonicalRoot, removed };
270
+ }
271
+ finally {
272
+ this.statusCache.delete(canonicalRoot);
273
+ this.workspaceRuntimeCache.delete(canonicalRoot);
274
+ try {
275
+ await this.runtimeManager.evict(canonicalRoot);
276
+ }
277
+ finally {
278
+ this.droppingRoots.delete(canonicalRoot);
279
+ }
280
+ }
281
+ }
282
+ async search(input, options = {}) {
283
+ const startedAt = Date.now();
284
+ const requestedRoot = await resolveRequestedRoot(input.root, false);
285
+ this.assertRootNotDropping(requestedRoot);
286
+ const runtime = await this.runtimeManager.activate(requestedRoot);
287
+ const releaseRuntimeActivity = runtime.beginActivity();
288
+ try {
289
+ const searchInfo = await this.inspectRootWithCache(runtime.canonicalRoot);
290
+ const currentModelLoadRequest = runtime.currentModelLoadRequest();
291
+ const defaultModelLoadRequest = searchInfo.indexed
292
+ ? this.searchModelLoadRequest(searchInfo, {})
293
+ : currentModelLoadRequest;
294
+ if (!defaultModelLoadRequest) {
295
+ throw new DaemonError("INDEX_MISSING", "Search requires an existing workspace index.");
296
+ }
297
+ const searchModelLoadRequest = searchInfo.indexed
298
+ ? this.searchModelLoadRequest(searchInfo, input)
299
+ : this.overrideActiveModelLoadRequest(defaultModelLoadRequest, input);
300
+ runtime.updateModelLoadRequest(defaultModelLoadRequest);
301
+ this.ensureWatcher(runtime);
302
+ await runtime.probeInitialFreshness(async () => {
303
+ const info = await this.inspectRoot(runtime.canonicalRoot, true);
304
+ this.statusCache.set(runtime.canonicalRoot, info);
305
+ return indexStatusIsFresh(info);
306
+ }, (initialFreshness) => {
307
+ this.options.logger?.event(`runtime.initial_probe_${initialFreshness}`, {
308
+ root_id: rootIdentity(runtime.canonicalRoot),
309
+ });
310
+ });
311
+ let updateJob;
312
+ const executeSearch = () => withRemoteEmbeddingOperationPermit(options.authorization, () => runtime.search({
313
+ queries: input.queries,
314
+ routes: input.routes,
315
+ fuse: input.fuse,
316
+ limit: input.limit,
317
+ trace: input.trace,
318
+ preferSymbol: input.preferSymbol,
319
+ symbolTypes: input.symbolTypes,
320
+ globs: normalizePlainStringList(input.globs),
321
+ insensitiveGlobs: normalizePlainStringList(input.insensitiveGlobs),
322
+ fileTypes: normalizePlainStringList(input.fileTypes),
323
+ excludedFileTypes: normalizePlainStringList(input.excludedFileTypes),
324
+ hidden: input.hidden,
325
+ noIgnore: input.noIgnore,
326
+ ignoreFiles: input.ignoreFiles,
327
+ maxDepth: input.maxDepth,
328
+ maxFileSizeBytes: input.maxFileSizeBytes,
329
+ follow: input.follow,
330
+ embeddingConcurrency: input.embeddingConcurrency,
331
+ modifiedAfter: input.modifiedAfter,
332
+ modifiedBefore: input.modifiedBefore,
333
+ autoUpdate: false,
334
+ }, searchModelLoadRequest));
335
+ let result;
336
+ if (input.freshness === "wait_for_fresh") {
337
+ while (true) {
338
+ updateJob =
339
+ (await this.waitForFresh(runtime, options.authorization, input)) ??
340
+ updateJob;
341
+ const beforeSearch = runtime.snapshot();
342
+ result = await executeSearch();
343
+ const afterSearch = runtime.snapshot();
344
+ if (!afterSearch.watcherPending &&
345
+ afterSearch.watcherEpoch === beforeSearch.watcherEpoch &&
346
+ !runtime.needsReconciliation()) {
347
+ break;
348
+ }
349
+ }
350
+ }
351
+ else {
352
+ result = await executeSearch();
353
+ }
354
+ if (runtime.needsReconciliation() &&
355
+ input.autoUpdate &&
356
+ (runtime.requiresFullReconciliation() ||
357
+ !this.scheduler.hasActiveRoot(runtime.canonicalRoot))) {
358
+ if (!runtime.requiresFullReconciliation() ||
359
+ runtime.canProbeFullReconciliation()) {
360
+ await this.probeCurrentFreshness(runtime);
361
+ }
362
+ const currentJob = this.scheduler.getByRoot(runtime.canonicalRoot);
363
+ const terminalKnownPathJob = !runtime.requiresFullReconciliation() &&
364
+ (currentJob?.state === "failed" || currentJob?.state === "cancelled");
365
+ if (runtime.needsReconciliation() && !terminalKnownPathJob) {
366
+ updateJob = await this.submitIndex(runtime, {
367
+ root: runtime.canonicalRoot,
368
+ apiKey: input.apiKey,
369
+ device: input.device,
370
+ runtimeOverridesAreEphemeral: true,
371
+ }, "background_reconcile", false, options.authorization);
372
+ }
373
+ }
374
+ const job = updateJob ?? this.scheduler.getByRoot(runtime.canonicalRoot);
375
+ const runtimeSnapshot = runtime.snapshot();
376
+ const freshness = runtime.needsReconciliation() ||
377
+ runtimeSnapshot.watcherPending ||
378
+ job?.state === "queued" ||
379
+ job?.state === "running"
380
+ ? "possibly_stale"
381
+ : "fresh";
382
+ const response = {
383
+ root: runtime.canonicalRoot,
384
+ freshness,
385
+ indexing: freshness === "possibly_stale"
386
+ ? searchIndexingSnapshot(job, this.currentIndexCompletion(runtime.canonicalRoot))
387
+ : undefined,
388
+ result,
389
+ };
390
+ this.options.logger?.event("search.completed", {
391
+ root_id: rootIdentity(runtime.canonicalRoot),
392
+ duration_ms: Date.now() - startedAt,
393
+ freshness: response.freshness,
394
+ result_count: result.items.length,
395
+ });
396
+ return response;
397
+ }
398
+ finally {
399
+ releaseRuntimeActivity();
400
+ }
401
+ }
402
+ currentIndexCompletion(canonicalRoot) {
403
+ return indexCompletionFromStatus(this.statusCache.get(canonicalRoot)?.status);
404
+ }
405
+ async indexStatus(input) {
406
+ const requestedCanonicalRoot = await resolveRequestedRoot(input.root, false);
407
+ let info;
408
+ try {
409
+ info = await inspectRoot(input.root, this.options.serviceOptions);
410
+ this.statusCache.set(requestedCanonicalRoot, info);
411
+ }
412
+ catch (error) {
413
+ const cached = this.statusCache.get(requestedCanonicalRoot);
414
+ if (!cached ||
415
+ !isEngineError(error) ||
416
+ error.code !== "ZVEC_GREP.ENGINE.LOCK.BUSY") {
417
+ throw error;
418
+ }
419
+ info = cached;
420
+ }
421
+ const canonicalRoot = await resolveRequestedRoot(info.root, false);
422
+ const runtime = this.runtimeManager.getByCanonicalRoot(canonicalRoot);
423
+ const runtimeSnapshot = runtime?.snapshot();
424
+ const job = this.scheduler.getByRoot(canonicalRoot);
425
+ return {
426
+ root: canonicalRoot,
427
+ indexed: info.indexed,
428
+ indexPolicy: info.indexPolicy,
429
+ source: info.source,
430
+ persistent: persistentStatus(info),
431
+ runtime: runtimeSnapshot
432
+ ? {
433
+ watcherActive: runtimeSnapshot.watcherActive,
434
+ dirtyRevision: runtimeSnapshot.dirtyRevision,
435
+ indexedRevision: runtimeSnapshot.indexedRevision,
436
+ activeJobId: job?.id,
437
+ jobState: job?.state,
438
+ progress: job?.progress ? formatProgress(job) : undefined,
439
+ completion: indexCompletionForJob(indexCompletionFromStatus(info.status), job?.state, job?.progress),
440
+ error: job?.error,
441
+ }
442
+ : undefined,
443
+ };
444
+ }
445
+ async serverStatus() {
446
+ const runtime = this.runtimeManager.snapshot();
447
+ const models = this.modelPool.snapshot();
448
+ const jobs = this.scheduler.snapshot();
449
+ return {
450
+ version: this.options.version,
451
+ uptimeMs: Date.now() - this.startedAt,
452
+ shuttingDown: this.shuttingDown,
453
+ activeRuntimes: runtime.activeRuntimes,
454
+ queuedJobs: jobs.queued,
455
+ runningJobs: jobs.running,
456
+ models,
457
+ };
458
+ }
459
+ async rg(input) {
460
+ const startedAt = Date.now();
461
+ const canonicalRoot = await resolveRequestedRoot(input.root, false);
462
+ const service = await (this.options.createService ?? createZvecGrep)({
463
+ ...this.options.serviceOptions,
464
+ root: canonicalRoot,
465
+ daemonInstanceToken: this.runtimeManager.instanceToken,
466
+ });
467
+ try {
468
+ const result = await service.context({
469
+ ...contextOptionsFromRgInput({ ...input, root: canonicalRoot }),
470
+ root: canonicalRoot,
471
+ autoUpdate: false,
472
+ });
473
+ this.options.logger?.event("rg.completed", {
474
+ root_id: rootIdentity(canonicalRoot),
475
+ duration_ms: Date.now() - startedAt,
476
+ result_count: result.items.length,
477
+ });
478
+ return {
479
+ root: canonicalRoot,
480
+ result,
481
+ };
482
+ }
483
+ finally {
484
+ await service.close();
485
+ }
486
+ }
487
+ async close() {
488
+ if (this.closePromise) {
489
+ return this.closePromise;
490
+ }
491
+ this.shuttingDown = true;
492
+ this.closePromise = (async () => {
493
+ await Promise.all([...this.watchers.values()].map((watcher) => watcher.close()));
494
+ for (const root of this.watchers.keys()) {
495
+ this.runtimeManager.getByCanonicalRoot(root)?.setWatcherActive(false);
496
+ }
497
+ this.watchers.clear();
498
+ this.indexCoordinators.clear();
499
+ await this.scheduler.close();
500
+ await this.runtimeManager.close();
501
+ await this.modelPool.close();
502
+ this.workspaceRuntimeCache.clear();
503
+ })();
504
+ return this.closePromise;
505
+ }
506
+ async runIndex(runtime, input, report, authorization, signal) {
507
+ const startedAt = Date.now();
508
+ const includeFinalStatus = !input.changedPaths;
509
+ const before = await inspectRoot(runtime.canonicalRoot, this.options.serviceOptions, input.rebuild !== true);
510
+ this.statusCache.set(runtime.canonicalRoot, before);
511
+ const modelLoadRequest = this.indexModelLoadRequest(before, input);
512
+ const model = modelLoadRequest.model;
513
+ runtime.updateModelLoadRequest(modelLoadRequest);
514
+ let lease;
515
+ try {
516
+ lease = await this.modelPool.acquire(modelLoadRequest);
517
+ }
518
+ catch (error) {
519
+ throw new DaemonError("MODEL_LOAD_FAILED", `Embedding model ${embeddingModelReference(model)} could not be created: ${error instanceof Error ? error.message : String(error)}`);
520
+ }
521
+ let service;
522
+ try {
523
+ service = await (this.options.createService ?? createZvecGrep)({
524
+ ...this.options.serviceOptions,
525
+ root: runtime.canonicalRoot,
526
+ embeddingModel: lease.model,
527
+ embeddingModelOwnership: "borrowed",
528
+ embedding: input.runtimeOverridesAreEphemeral
529
+ ? undefined
530
+ : input.embedding,
531
+ apiKey: input.runtimeOverridesAreEphemeral ? undefined : input.apiKey,
532
+ endpoint: input.runtimeOverridesAreEphemeral
533
+ ? undefined
534
+ : input.endpoint,
535
+ device: input.runtimeOverridesAreEphemeral ? undefined : input.device,
536
+ daemonInstanceToken: this.runtimeManager.instanceToken,
537
+ });
538
+ await withRemoteEmbeddingOperationPermit(authorization, () => service.index({
539
+ root: runtime.canonicalRoot,
540
+ rebuild: input.rebuild,
541
+ resetPaths: input.resetPaths,
542
+ globs: normalizePlainStringList(input.globs),
543
+ insensitiveGlobs: normalizePlainStringList(input.insensitiveGlobs),
544
+ fileTypes: normalizePlainStringList(input.fileTypes),
545
+ excludedFileTypes: normalizePlainStringList(input.excludedFileTypes),
546
+ hidden: input.hidden,
547
+ noIgnore: input.noIgnore,
548
+ ignoreFiles: normalizePlainStringList(input.ignoreFiles),
549
+ maxDepth: input.maxDepth,
550
+ maxFileSizeBytes: input.maxFileSizeBytes,
551
+ follow: input.follow,
552
+ embeddingConcurrency: input.embeddingConcurrency,
553
+ changedPaths: input.changedPaths,
554
+ signal,
555
+ onProgress: report,
556
+ onWriterContext: (context) => runtime.setWriterContext(context, lease.key),
557
+ }));
558
+ }
559
+ finally {
560
+ try {
561
+ await service?.close();
562
+ }
563
+ finally {
564
+ lease.release();
565
+ }
566
+ }
567
+ if (includeFinalStatus) {
568
+ await this.watchers.get(runtime.canonicalRoot)?.flushPending();
569
+ }
570
+ const proofReconciliationEpoch = runtime.reconciliationEpoch();
571
+ const after = await inspectRoot(runtime.canonicalRoot, this.options.serviceOptions, includeFinalStatus);
572
+ if (includeFinalStatus)
573
+ this.statusCache.set(runtime.canonicalRoot, after);
574
+ if (!after.workspaceIndex?.embedding) {
575
+ throw new DaemonError("INDEX_MISSING", "Index completed without an embedding schema.");
576
+ }
577
+ runtime.updateModelLoadRequest(this.searchModelLoadRequest(after, {}));
578
+ this.options.logger?.event("index.completed", {
579
+ root_id: rootIdentity(runtime.canonicalRoot),
580
+ duration_ms: Date.now() - startedAt,
581
+ scope: input.changedPaths ? "paths" : "reconcile",
582
+ changed_paths: input.changedPaths?.length,
583
+ });
584
+ const reconciled = includeFinalStatus && indexStatusIsFresh(after);
585
+ if (includeFinalStatus && !reconciled) {
586
+ runtime.requireFullReconciliation(true);
587
+ }
588
+ return {
589
+ reconciled,
590
+ reconciliationEpoch: proofReconciliationEpoch,
591
+ };
592
+ }
593
+ async submitIndex(runtime, input, reason, wait, authorization) {
594
+ const createsWork = !this.scheduler.hasActiveRoot(runtime.canonicalRoot) ||
595
+ input.rebuild === true;
596
+ const targetRevision = createsWork
597
+ ? runtime.markDirty()
598
+ : runtime.snapshot().dirtyRevision;
599
+ const followsNarrowWatch = this.scheduler.getByRoot(runtime.canonicalRoot)?.reason === "watch";
600
+ runtime.setWriterPending(true);
601
+ let submitted;
602
+ try {
603
+ submitted = this.scheduler.submit({
604
+ canonicalRoot: runtime.canonicalRoot,
605
+ reason,
606
+ followupIfRunning: followsNarrowWatch,
607
+ run: (report, signal) => runtime.withWrite(async () => {
608
+ const proof = await this.runIndex(runtime, input, report, authorization, signal);
609
+ if (proof.reconciled) {
610
+ runtime.markReconciled(targetRevision, proof.reconciliationEpoch);
611
+ }
612
+ else {
613
+ runtime.markIndexed(targetRevision);
614
+ }
615
+ }),
616
+ });
617
+ }
618
+ catch (error) {
619
+ runtime.setWriterPending(false);
620
+ throw error;
621
+ }
622
+ if (!submitted.reused) {
623
+ void this.scheduler.waitForRootIdle(runtime.canonicalRoot).finally(() => {
624
+ runtime.setWriterPending(false);
625
+ });
626
+ }
627
+ if (!wait)
628
+ return submitted.job;
629
+ const job = await this.scheduler.wait(submitted.job.id);
630
+ await this.scheduler.waitForRootIdle(runtime.canonicalRoot);
631
+ runtime.setWriterPending(false);
632
+ return job;
633
+ }
634
+ ensureWatcher(runtime) {
635
+ if (this.watchers.has(runtime.canonicalRoot) || this.shuttingDown) {
636
+ return;
637
+ }
638
+ const coordinator = new IndexCoordinator({
639
+ runtime,
640
+ scheduler: this.scheduler,
641
+ getIndexedFileCount: () => this.statusCache.get(runtime.canonicalRoot)?.status?.filesStored,
642
+ run: async (changes, report, signal) => {
643
+ const changedPaths = [
644
+ ...changes.touchedFiles,
645
+ ...changes.rescanDirectories,
646
+ ...changes.deletedPrefixes,
647
+ ];
648
+ if (!changes.forceFullReconcile && changedPaths.length === 0) {
649
+ return;
650
+ }
651
+ const automaticAuthorization = await this.automaticIndexAuthorization(runtime);
652
+ if (!automaticAuthorization.allowed) {
653
+ throw new DaemonError("REMOTE_EMBEDDING_AUTH_REQUIRED", "A Workspace Remote Embedding grant is required for file-watcher index updates.");
654
+ }
655
+ return await this.runIndex(runtime, {
656
+ root: runtime.canonicalRoot,
657
+ changedPaths: changes.forceFullReconcile ? undefined : changedPaths,
658
+ }, report, automaticAuthorization.authorization, signal);
659
+ },
660
+ });
661
+ const watcher = (this.options.watchManagerFactory ??
662
+ ((options) => new WatchManager(options)))({
663
+ root: runtime.canonicalRoot,
664
+ onChanges: async (changes, reason) => {
665
+ const pathCount = changes.touchedFiles.length +
666
+ changes.rescanDirectories.length +
667
+ changes.deletedPrefixes.length;
668
+ if (changes.forceFullReconcile && pathCount === 0) {
669
+ runtime.requireFullReconciliation(true);
670
+ this.options.logger?.event("watcher.reconciliation_probe_requested", {
671
+ root_id: rootIdentity(runtime.canonicalRoot),
672
+ reason,
673
+ active_job: this.scheduler.hasActiveRoot(runtime.canonicalRoot),
674
+ });
675
+ return;
676
+ }
677
+ const automaticAuthorization = await this.automaticIndexAuthorization(runtime);
678
+ if (!automaticAuthorization.allowed) {
679
+ runtime.markDirty();
680
+ runtime.requireFullReconciliation();
681
+ this.options.logger?.event("watcher.authorization_required", {
682
+ root_id: rootIdentity(runtime.canonicalRoot),
683
+ reason,
684
+ });
685
+ return;
686
+ }
687
+ this.options.logger?.event(changes.forceFullReconcile ? "watcher.overflow" : "watcher.changes", {
688
+ root_id: rootIdentity(runtime.canonicalRoot),
689
+ reason,
690
+ touched_files: changes.touchedFiles.length,
691
+ rescan_directories: changes.rescanDirectories.length,
692
+ deleted_prefixes: changes.deletedPrefixes.length,
693
+ });
694
+ coordinator.enqueue(changes, reason);
695
+ },
696
+ onPendingChange: (pending) => runtime.setWatcherPending(pending),
697
+ });
698
+ watcher.start();
699
+ this.indexCoordinators.set(runtime.canonicalRoot, coordinator);
700
+ this.watchers.set(runtime.canonicalRoot, watcher);
701
+ runtime.setWatcherActive(true);
702
+ }
703
+ async settleKnownChanges(runtime) {
704
+ const watcher = this.watchers.get(runtime.canonicalRoot);
705
+ while (true) {
706
+ await watcher?.flushPending();
707
+ await this.scheduler.waitForRootIdle(runtime.canonicalRoot);
708
+ const job = this.scheduler.getByRoot(runtime.canonicalRoot);
709
+ const snapshot = runtime.snapshot();
710
+ if (job?.state === "failed" ||
711
+ job?.state === "cancelled" ||
712
+ !snapshot.watcherPending) {
713
+ return;
714
+ }
715
+ }
716
+ }
717
+ async automaticIndexAuthorization(runtime) {
718
+ const knownProvider = runtime.embeddingProvider();
719
+ if (knownProvider && knownProvider !== "qwen")
720
+ return { allowed: true };
721
+ const root = runtime.canonicalRoot;
722
+ const info = await inspectRoot(root, this.options.serviceOptions);
723
+ const schema = info.workspaceIndex?.embedding;
724
+ if (!schema || schema.provider !== "qwen")
725
+ return { allowed: true };
726
+ const modelInfo = await this.loadEmbeddingModelInfo(this.searchModelLoadRequest(info, {}));
727
+ const plan = await planRemoteIndexAuthorization({
728
+ info,
729
+ model: modelInfo,
730
+ needsUpdate: true,
731
+ store: this.authorizationManager.store,
732
+ });
733
+ if (!plan)
734
+ return { allowed: true };
735
+ const authorization = await this.authorizationManager.existingWorkspacePermit(plan);
736
+ return authorization
737
+ ? { allowed: true, authorization }
738
+ : { allowed: false };
739
+ }
740
+ async waitForFresh(runtime, authorization, runtimeOverrides = {}) {
741
+ let updateJob;
742
+ while (true) {
743
+ await this.settleKnownChanges(runtime);
744
+ if (!runtime.needsReconciliation()) {
745
+ return updateJob;
746
+ }
747
+ if (!runtime.requiresFullReconciliation() ||
748
+ runtime.canProbeFullReconciliation() ||
749
+ this.scheduler.getByRoot(runtime.canonicalRoot)?.state === "failed" ||
750
+ this.scheduler.getByRoot(runtime.canonicalRoot)?.state === "cancelled") {
751
+ const freshness = await this.probeCurrentFreshness(runtime);
752
+ if (freshness === "fresh") {
753
+ return updateJob;
754
+ }
755
+ }
756
+ const currentJob = this.scheduler.getByRoot(runtime.canonicalRoot);
757
+ if (!runtime.requiresFullReconciliation() &&
758
+ (currentJob?.state === "failed" || currentJob?.state === "cancelled")) {
759
+ throw new DaemonError(currentJob.error?.code ?? "INDEX_FAILED", currentJob.error?.message ??
760
+ "Known index changes did not complete successfully.");
761
+ }
762
+ updateJob = await this.submitIndex(runtime, {
763
+ root: runtime.canonicalRoot,
764
+ apiKey: runtimeOverrides.apiKey,
765
+ device: runtimeOverrides.device,
766
+ runtimeOverridesAreEphemeral: true,
767
+ }, "fresh_query", true, authorization);
768
+ if (updateJob.state !== "succeeded") {
769
+ throw new DaemonError(updateJob.error?.code ?? "INDEX_FAILED", updateJob.error?.message ??
770
+ "Index reconciliation did not complete successfully.");
771
+ }
772
+ }
773
+ }
774
+ async loadEmbeddingModelInfo(request) {
775
+ const lease = await this.modelPool.acquire(request);
776
+ try {
777
+ return lease.model.info;
778
+ }
779
+ finally {
780
+ lease.release();
781
+ }
782
+ }
783
+ async probeCurrentFreshness(runtime) {
784
+ const freshness = await runtime.probeFreshness(async () => {
785
+ const info = await inspectRoot(runtime.canonicalRoot, this.options.serviceOptions);
786
+ this.statusCache.set(runtime.canonicalRoot, info);
787
+ return indexStatusIsFresh(info);
788
+ });
789
+ this.options.logger?.event(`runtime.recovery_probe_${freshness}`, {
790
+ root_id: rootIdentity(runtime.canonicalRoot),
791
+ });
792
+ return freshness;
793
+ }
794
+ async closeWatcher(canonicalRoot) {
795
+ const watcher = this.watchers.get(canonicalRoot);
796
+ this.watchers.delete(canonicalRoot);
797
+ this.indexCoordinators.delete(canonicalRoot);
798
+ await watcher?.close();
799
+ }
800
+ assertRootNotDropping(canonicalRoot) {
801
+ if (this.droppingRoots.has(canonicalRoot)) {
802
+ throw new DaemonError("INDEX_DROP_IN_PROGRESS", "The Workspace index is being dropped.", true);
803
+ }
804
+ }
805
+ indexModel(info, input) {
806
+ if (info.workspaceIndex?.embedding && !input.embedding) {
807
+ return {
808
+ provider: info.workspaceIndex.embedding.provider,
809
+ name: info.workspaceIndex.embedding.model,
810
+ };
811
+ }
812
+ const reference = resolveEmbeddingReference({
813
+ explicit: input.embedding ?? this.options.serviceOptions?.embedding,
814
+ environment: {
815
+ ZVEC_GREP_EMBEDDING: input.embeddingEnvironment ?? process.env.ZVEC_GREP_EMBEDDING,
816
+ },
817
+ globalDefault: readGlobalConfig().defaults?.embedding,
818
+ fallback: this.options.serviceOptions?.defaultEmbedding
819
+ ? DEFAULT_LOCAL_EMBEDDING
820
+ : undefined,
821
+ });
822
+ if (!reference) {
823
+ throw new DaemonError("MODEL_LOAD_FAILED", "A new index requires embedding or an explicit server default model.");
824
+ }
825
+ return parseEmbeddingModelReference(reference);
826
+ }
827
+ indexModelLoadRequest(info, input) {
828
+ const model = this.indexModel(info, input);
829
+ const workspaceRuntime = info.workspaceIndex?.embedding?.provider === model.provider
830
+ ? this.readWorkspaceEmbeddingRuntime(info)
831
+ : {};
832
+ const runtime = this.resolveModelRuntime(model, workspaceRuntime, input);
833
+ if (input.rebuild !== true &&
834
+ info.workspaceIndex?.embedding &&
835
+ workspaceRuntime.endpoint !== runtime.endpoint) {
836
+ throw new DaemonError("EMBEDDING_ENDPOINT_MISMATCH", "The requested embedding endpoint differs from the workspace snapshot; use rebuild to change endpoints.");
837
+ }
838
+ return { model, runtime };
839
+ }
840
+ searchModelLoadRequest(info, overrides) {
841
+ const schema = info.workspaceIndex?.embedding;
842
+ if (!info.indexed || !schema) {
843
+ throw new DaemonError("INDEX_MISSING", "Search requires an existing workspace index.");
844
+ }
845
+ const model = {
846
+ provider: schema.provider,
847
+ name: schema.model,
848
+ };
849
+ const workspaceRuntime = this.readWorkspaceEmbeddingRuntime(info);
850
+ const runtime = this.resolveModelRuntime(model, workspaceRuntime, overrides);
851
+ return { model, runtime };
852
+ }
853
+ resolveModelRuntime(model, workspaceRuntime, overrides) {
854
+ const serviceOptions = this.options.serviceOptions;
855
+ return resolveEmbeddingRuntimeOptions(embeddingModelReference(model), {
856
+ apiKey: overrides.apiKey ?? serviceOptions?.apiKey,
857
+ endpoint: overrides.endpoint ?? serviceOptions?.endpoint,
858
+ device: overrides.device ?? serviceOptions?.device,
859
+ }, workspaceRuntime, readGlobalConfig());
860
+ }
861
+ overrideActiveModelLoadRequest(request, overrides) {
862
+ const runtime = resolveEmbeddingRuntimeOptions(embeddingModelReference(request.model), overrides, request.runtime ?? {}, readGlobalConfig());
863
+ return { model: request.model, runtime };
864
+ }
865
+ readWorkspaceEmbeddingRuntime(info) {
866
+ try {
867
+ const runtime = readWorkspaceEmbeddingRuntime(info);
868
+ this.workspaceRuntimeCache.set(info.root, runtime);
869
+ return runtime;
870
+ }
871
+ catch (error) {
872
+ const cached = this.workspaceRuntimeCache.get(info.root);
873
+ if (cached &&
874
+ isEngineError(error) &&
875
+ (error.code === "ZVEC_GREP.ENGINE.STORAGE.ZVEC_OPEN_FAILED" ||
876
+ error.code === "ZVEC_GREP.ENGINE.LOCK.BUSY")) {
877
+ return cached;
878
+ }
879
+ throw error;
880
+ }
881
+ }
882
+ async inspectRootWithCache(root) {
883
+ const cached = this.statusCache.get(root);
884
+ if (cached) {
885
+ return cached;
886
+ }
887
+ try {
888
+ const info = await this.inspectRoot(root, false);
889
+ this.statusCache.set(root, info);
890
+ return info;
891
+ }
892
+ catch (error) {
893
+ const fallback = this.statusCache.get(root);
894
+ if (fallback &&
895
+ isEngineError(error) &&
896
+ error.code === "ZVEC_GREP.ENGINE.LOCK.BUSY") {
897
+ return fallback;
898
+ }
899
+ throw error;
900
+ }
901
+ }
902
+ async inspectRoot(root, includeStatus) {
903
+ return await (this.options.inspectRoot ?? inspectRoot)(root, this.options.serviceOptions, includeStatus);
904
+ }
905
+ }
906
+ function readWorkspaceEmbeddingRuntime(info) {
907
+ if (!info.workspaceIndex)
908
+ return {};
909
+ const location = workspaceIndexLocation(info.root);
910
+ return readWorkspaceManifest(location.home)?.embeddingRuntime ?? {};
911
+ }
912
+ function indexStatusIsFresh(info) {
913
+ const status = info.status;
914
+ return (status !== null &&
915
+ status !== undefined &&
916
+ status.filesAdded === 0 &&
917
+ status.filesModified === 0 &&
918
+ status.filesDeleted === 0 &&
919
+ status.filesPending === 0 &&
920
+ status.filesFailed === 0);
921
+ }
922
+ function assertDropOnlyInput(input) {
923
+ const conflicts = [
924
+ [input.embedding !== undefined, "embedding"],
925
+ [input.apiKey !== undefined, "apiKey"],
926
+ [input.endpoint !== undefined, "endpoint"],
927
+ [input.device !== undefined, "device"],
928
+ [input.rebuild !== undefined, "rebuild"],
929
+ [input.resetPaths !== undefined, "resetPaths"],
930
+ [input.globs !== undefined, "globs"],
931
+ [input.insensitiveGlobs !== undefined, "insensitiveGlobs"],
932
+ [input.fileTypes !== undefined, "fileTypes"],
933
+ [input.excludedFileTypes !== undefined, "excludedFileTypes"],
934
+ [input.hidden !== undefined, "hidden"],
935
+ [input.noIgnore !== undefined, "noIgnore"],
936
+ [input.ignoreFiles !== undefined, "ignoreFiles"],
937
+ [input.maxDepth !== undefined, "maxDepth"],
938
+ [input.maxFileSizeBytes !== undefined, "maxFileSizeBytes"],
939
+ [input.follow !== undefined, "follow"],
940
+ [input.embeddingConcurrency !== undefined, "embeddingConcurrency"],
941
+ [input.wait !== undefined, "wait"],
942
+ ];
943
+ const names = conflicts
944
+ .filter(([conflictsWithDrop]) => conflictsWithDrop)
945
+ .map(([, name]) => name);
946
+ if (names.length > 0) {
947
+ throw new DaemonError("INVALID_ARGUMENT", `zvec_grep_index drop cannot be combined with ${names.join(", ")}.`);
948
+ }
949
+ }
950
+ function embeddingSchema(model) {
951
+ return {
952
+ provider: model.info.provider,
953
+ model: model.info.name,
954
+ dimension: model.info.dimension,
955
+ metric: model.info.metric,
956
+ };
957
+ }
958
+ function parseEmbeddingModelReference(reference) {
959
+ const separator = reference.indexOf("/");
960
+ if (separator <= 0 || separator === reference.length - 1) {
961
+ throw new DaemonError("MODEL_LOAD_FAILED", `Embedding model reference ${reference} is invalid.`);
962
+ }
963
+ return {
964
+ provider: reference.slice(0, separator),
965
+ name: reference.slice(separator + 1),
966
+ };
967
+ }
968
+ function embeddingModelReference(model) {
969
+ return `${model.provider}/${model.name}`;
970
+ }
971
+ function persistentStatus(info) {
972
+ return {
973
+ home: info.home,
974
+ index_path: info.indexPath,
975
+ workspace_index: info.workspaceIndex
976
+ ? {
977
+ id: info.workspaceIndex.id,
978
+ name: info.workspaceIndex.name,
979
+ path: info.workspaceIndex.path,
980
+ root_paths: info.workspaceIndex.rootPaths.map((rootPath) => ({
981
+ absolute_path: rootPath.absolutePath,
982
+ recursive: rootPath.recursive,
983
+ include: rootPath.include ? [...rootPath.include] : undefined,
984
+ exclude: rootPath.exclude ? [...rootPath.exclude] : undefined,
985
+ globs: rootPath.globs ? [...rootPath.globs] : undefined,
986
+ insensitive_globs: rootPath.insensitiveGlobs
987
+ ? [...rootPath.insensitiveGlobs]
988
+ : undefined,
989
+ file_types: rootPath.fileTypes
990
+ ? [...rootPath.fileTypes]
991
+ : undefined,
992
+ excluded_file_types: rootPath.excludedFileTypes
993
+ ? [...rootPath.excludedFileTypes]
994
+ : undefined,
995
+ hidden: rootPath.hidden,
996
+ no_ignore: rootPath.noIgnore,
997
+ ignore_files: rootPath.ignoreFiles
998
+ ? [...rootPath.ignoreFiles]
999
+ : undefined,
1000
+ max_depth: rootPath.maxDepth,
1001
+ max_file_size_bytes: rootPath.maxFileSizeBytes,
1002
+ follow: rootPath.follow,
1003
+ })),
1004
+ embedding: info.workspaceIndex.embedding,
1005
+ index_version: info.workspaceIndex.indexVersion,
1006
+ created_time: info.workspaceIndex.createdTime,
1007
+ updated_time: info.workspaceIndex.updatedTime,
1008
+ }
1009
+ : undefined,
1010
+ files: info.status
1011
+ ? {
1012
+ stored: info.status.filesStored,
1013
+ scanned: info.status.filesScanned,
1014
+ indexed: info.status.filesIndexed,
1015
+ pending: info.status.filesPending,
1016
+ failed: info.status.filesFailed,
1017
+ added: info.status.filesAdded,
1018
+ modified: info.status.filesModified,
1019
+ deleted: info.status.filesDeleted,
1020
+ unchanged: info.status.filesUnchanged,
1021
+ entities: info.status.entitiesIndexed,
1022
+ truncated_fragments: info.status.fragmentsTruncated,
1023
+ }
1024
+ : undefined,
1025
+ suggestion: info.suggestion,
1026
+ };
1027
+ }
1028
+ function formatProgress(job) {
1029
+ const progress = job.progress;
1030
+ if (!progress) {
1031
+ return undefined;
1032
+ }
1033
+ return {
1034
+ phase: progress.phase,
1035
+ files_total: progress.filesTotal,
1036
+ files_indexed: progress.filesIndexed,
1037
+ files_failed: progress.filesFailed,
1038
+ detail: progress.detail,
1039
+ };
1040
+ }
1041
+ function searchIndexingSnapshot(job, completion) {
1042
+ const state = !job || job.state === "succeeded" ? "idle" : job.state;
1043
+ const effectiveCompletion = indexCompletionForJob(completion, job?.state, job?.progress);
1044
+ return {
1045
+ state,
1046
+ ...effectiveCompletion,
1047
+ };
1048
+ }
1049
+ //# sourceMappingURL=backend.js.map