@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
@@ -1,12 +1,12 @@
1
- import { readFile } from "node:fs/promises";
2
- import { collectionDetail, detail, EngineError, errorDetails, isEngineError, } from "../../errors/index.js";
1
+ import { lstat, readFile } from "node:fs/promises";
2
+ import { workspaceIndexDetail, detail, EngineError, errorDetails, isEngineError, } from "../../errors.js";
3
3
  import { sha256Bytes } from "../../utils/hash.js";
4
+ import { normalizePath } from "../../utils/path.js";
4
5
  import { ConcurrentTiming, TimingCollector } from "../../utils/timing.js";
5
- import { ExtractorRegistry } from "../../extraction/index.js";
6
- import { scanRootPaths } from "./scanner/index.js";
7
- const DEFAULT_EMBEDDING_CONCURRENCY = 4;
8
- const DEFAULT_EMBEDDING_MIN_CONCURRENCY = 4;
9
- const DEFAULT_EMBEDDING_MAX_CONCURRENCY = 12;
6
+ import { extract, } from "../../extraction/index.js";
7
+ import { scanDirectoryPath, scanFilePath, scanRootPaths, } from "./scanner/index.js";
8
+ const APPROXIMATE_CHARS_PER_TOKEN = 2;
9
+ const CHUNK_OVERLAP_RATIO = 0.15;
10
10
  const EMBEDDING_TRANSIENT_MAX_RETRIES = 3;
11
11
  const EMBEDDING_RATE_LIMIT_MAX_RETRIES = 6;
12
12
  const EMBEDDING_TRANSIENT_RETRY_BASE_DELAY_MS = 500;
@@ -15,33 +15,44 @@ const EMBEDDING_TRANSIENT_RETRY_MAX_DELAY_MS = 8000;
15
15
  const EMBEDDING_RATE_LIMIT_RETRY_MAX_DELAY_MS = 30000;
16
16
  const EMBEDDING_RETRY_JITTER_MS = 500;
17
17
  const EMBEDDING_SUCCESS_STREAK_MIN = 4;
18
- export async function indexCollection(ctx) {
18
+ export async function indexWorkspace(ctx) {
19
19
  try {
20
- return await indexCollectionUnchecked(ctx);
20
+ return await indexWorkspaceUnchecked(ctx);
21
21
  }
22
22
  catch (error) {
23
- throw toEngineError(error, "Indexing collection failed", {
24
- code: "ZVEC_GREP.ENGINE.INDEXING.COLLECTION_FAILED",
25
- context: collectionContext(ctx.collection),
23
+ throw toEngineError(error, "Indexing workspace failed", {
24
+ code: "ZVEC_GREP.ENGINE.INDEXING.WORKSPACE_FAILED",
25
+ context: workspaceIndexContext(ctx.workspaceIndex),
26
26
  });
27
27
  }
28
28
  }
29
- export async function getCollectionIndexStatus(collection, storedFiles) {
29
+ export async function indexWorkspacePaths(ctx, changedPaths) {
30
30
  try {
31
- const scan = await scanRootPaths(collection.id, collection.rootPaths);
31
+ return await indexWorkspacePathsUnchecked(ctx, changedPaths);
32
+ }
33
+ catch (error) {
34
+ throw toEngineError(error, "Indexing changed paths failed", {
35
+ code: "ZVEC_GREP.ENGINE.INDEXING.WORKSPACE_FAILED",
36
+ context: workspaceIndexContext(ctx.workspaceIndex),
37
+ });
38
+ }
39
+ }
40
+ export async function getWorkspaceIndexStatus(workspaceIndex, storedFiles) {
41
+ try {
42
+ const scan = await scanRootPaths(workspaceIndex.id, workspaceIndex.rootPaths);
32
43
  const diff = await computeDiffFromFiles(scan.files, storedFiles);
33
44
  const pendingFiles = storedFiles.filter((file) => file.indexStatus?.indexedTime === null);
34
45
  const failedFiles = pendingFiles.filter((file) => file.indexStatus?.error !== undefined);
35
46
  const indexedFiles = storedFiles.filter((file) => file.indexStatus?.indexedTime !== undefined &&
36
47
  file.indexStatus.indexedTime !== null);
37
48
  const entitiesIndexed = indexedFiles.reduce((count, file) => count + (file.indexStatus?.entityCount ?? 0), 0);
49
+ const fragmentsTruncated = indexedFiles.reduce((count, file) => count + (file.indexStatus?.truncatedFragmentCount ?? 0), 0);
38
50
  return {
39
- collectionId: collection.id,
40
- collectionName: collection.name,
41
51
  filesScanned: scan.files.length,
42
52
  filesStored: storedFiles.length,
43
53
  filesIndexed: indexedFiles.length,
44
54
  entitiesIndexed,
55
+ fragmentsTruncated,
45
56
  filesPending: pendingFiles.length,
46
57
  filesFailed: failedFiles.length,
47
58
  filesAdded: diff.added.length,
@@ -56,36 +67,42 @@ export async function getCollectionIndexStatus(collection, storedFiles) {
56
67
  };
57
68
  }
58
69
  catch (error) {
59
- throw toEngineError(error, "Inspecting collection index status failed", {
70
+ throw toEngineError(error, "Inspecting workspace index status failed", {
60
71
  code: "ZVEC_GREP.ENGINE.INDEXING.STATUS_FAILED",
61
- context: collectionContext(collection),
72
+ context: workspaceIndexContext(workspaceIndex),
62
73
  });
63
74
  }
64
75
  }
65
- async function indexCollectionUnchecked(ctx) {
76
+ async function indexWorkspaceUnchecked(ctx) {
66
77
  const start = Date.now();
67
78
  const report = ctx.onProgress ?? (() => undefined);
68
79
  const timings = new TimingCollector();
80
+ throwIfIndexCancelled(ctx);
69
81
  const firstPass = await runIndexPass(ctx, report, "Scanning files...", timings);
70
82
  const passes = [firstPass];
83
+ let progressBase;
71
84
  if (firstPass.stats.filesFailed > 0) {
72
85
  const failed = firstPass.stats.filesFailed;
86
+ progressBase = retryProgressBase(firstPass);
73
87
  report({
74
88
  phase: "scanning",
89
+ filesTotal: progressBase.filesTotal,
90
+ filesIndexed: progressBase.filesSucceeded,
75
91
  detail: `Retrying ${failed} failed ${failed === 1 ? "file" : "files"}...`,
76
92
  });
77
- passes.push(await runIndexPass(ctx, report, "Scanning retry candidates...", timings));
93
+ passes.push(await runIndexPass(ctx, report, "Scanning retry candidates...", timings, progressBase));
78
94
  }
79
95
  const finalPass = passes[passes.length - 1];
80
- reportIndexFinalizing(ctx, report, finalPass);
81
- timings.timeSync("index_optimize", () => optimizeStorage(ctx));
96
+ throwIfIndexCancelled(ctx);
97
+ reportIndexFinalizing(ctx, report, finalPass, progressBase);
98
+ await timings.time("index_optimize", () => optimizeStorage(ctx));
82
99
  const result = buildIndexResult(ctx, passes, Date.now() - start, timings);
83
100
  if (result.filesFailed > 0) {
84
101
  report({ phase: "done", detail: "Indexing completed with failed files" });
85
102
  throw new EngineError(`Indexing completed with ${result.filesFailed} failed ${result.filesFailed === 1 ? "file" : "files"}`, {
86
103
  code: "ZVEC_GREP.ENGINE.INDEXING.FILES_FAILED",
87
104
  context: errorDetails([
88
- collectionDetail(ctx.collection.name),
105
+ workspaceIndexDetail(ctx.workspaceIndex.name),
89
106
  detail("filesFailed", result.filesFailed),
90
107
  detail("filesScanned", result.filesScanned),
91
108
  detail("failedFiles", summarizeFailedFiles(finalPass.stats.failedFiles)),
@@ -99,18 +116,78 @@ async function indexCollectionUnchecked(ctx) {
99
116
  report({ phase: "done", detail: "Indexing complete" });
100
117
  return result;
101
118
  }
102
- async function runIndexPass(ctx, report, scanningDetail, timings) {
103
- report({ phase: "scanning", detail: scanningDetail });
104
- const scan = await timings.time("index_scan", () => scanRootPaths(ctx.collection.id, ctx.collection.rootPaths));
105
- const diff = await timings.time("index_diff", () => computeDiff(scan.files, ctx.storage));
119
+ async function indexWorkspacePathsUnchecked(ctx, changedPaths) {
120
+ const start = Date.now();
121
+ const report = ctx.onProgress ?? (() => undefined);
122
+ const timings = new TimingCollector();
123
+ const normalizedPaths = [...new Set(changedPaths.map(normalizePath))];
124
+ throwIfIndexCancelled(ctx);
125
+ const firstPass = await runPathIndexPass(ctx, report, normalizedPaths, timings);
126
+ const passes = [firstPass];
127
+ let progressBase;
128
+ if (firstPass.stats.filesFailed > 0) {
129
+ progressBase = retryProgressBase(firstPass);
130
+ passes.push(await runPathIndexPass(ctx, report, normalizedPaths, timings, progressBase));
131
+ }
132
+ const finalPass = passes[passes.length - 1];
133
+ throwIfIndexCancelled(ctx);
134
+ reportIndexFinalizing(ctx, report, finalPass, progressBase);
135
+ await timings.time("index_optimize", () => optimizeStorage(ctx));
136
+ const result = buildIndexResult(ctx, passes, Date.now() - start, timings);
137
+ if (result.filesFailed > 0) {
138
+ throw new EngineError(`Indexing completed with ${result.filesFailed} failed files`, {
139
+ code: "ZVEC_GREP.ENGINE.INDEXING.FILES_FAILED",
140
+ context: workspaceIndexContext(ctx.workspaceIndex),
141
+ });
142
+ }
143
+ report({ phase: "done", detail: "Indexing complete" });
144
+ return result;
145
+ }
146
+ async function runPathIndexPass(ctx, report, changedPaths, timings, progressBase) {
147
+ reportScanning(report, "Scanning changed paths...", progressBase);
148
+ const scanned = await timings.time("index_scan_paths", async () => {
149
+ const files = [];
150
+ for (const path of changedPaths) {
151
+ throwIfIndexCancelled(ctx);
152
+ const info = await lstat(path).catch(() => null);
153
+ const scan = info?.isDirectory()
154
+ ? await scanDirectoryPath(ctx.workspaceIndex.id, ctx.workspaceIndex.rootPaths, path, { signal: ctx.signal })
155
+ : await scanFilePath(ctx.workspaceIndex.id, ctx.workspaceIndex.rootPaths, path, {
156
+ signal: ctx.signal,
157
+ });
158
+ files.push(...scan.files);
159
+ }
160
+ return [...new Map(files.map((file) => [file.id, file])).values()];
161
+ });
162
+ throwIfIndexCancelled(ctx);
163
+ const existing = [
164
+ ...new Map(changedPaths
165
+ .flatMap((path) => ctx.storage.listFilesByPathPrefix(path))
166
+ .map((file) => [file.id, file])).values(),
167
+ ];
168
+ return runDiffPass(ctx, report, scanned, existing, timings, progressBase);
169
+ }
170
+ async function runIndexPass(ctx, report, scanningDetail, timings, progressBase) {
171
+ reportScanning(report, scanningDetail, progressBase);
172
+ const scan = await timings.time("index_scan", () => scanRootPaths(ctx.workspaceIndex.id, ctx.workspaceIndex.rootPaths, {
173
+ signal: ctx.signal,
174
+ }));
175
+ throwIfIndexCancelled(ctx);
176
+ return runDiffPass(ctx, report, scan.files, ctx.storage.listFiles(), timings, progressBase);
177
+ }
178
+ async function runDiffPass(ctx, report, scannedFiles, existingFiles, timings, progressBase) {
179
+ const diff = await timings.time("index_diff", () => computeDiffFromFiles(scannedFiles, existingFiles));
180
+ throwIfIndexCancelled(ctx);
106
181
  const pending = [...diff.added, ...diff.modified, ...diff.pending];
107
182
  report({
108
183
  phase: "scanning",
109
- filesTotal: scan.files.length,
184
+ filesTotal: progressBase?.filesTotal ?? scannedFiles.length,
185
+ filesIndexed: progressBase?.filesSucceeded ?? 0,
110
186
  detail: `${diff.added.length} added, ${diff.modified.length} modified, ${diff.pending.length} pending, ${diff.deleted.length} deleted, ${diff.unchanged.length} unchanged`,
111
187
  });
112
188
  timings.timeSync("index_delete_stale", () => {
113
189
  for (const file of diff.deleted) {
190
+ throwIfIndexCancelled(ctx);
114
191
  try {
115
192
  ctx.storage.deleteFile(file.id);
116
193
  }
@@ -125,8 +202,10 @@ async function runIndexPass(ctx, report, scanningDetail, timings) {
125
202
  const reportIndexing = (currentStats, detail, embedding) => {
126
203
  report({
127
204
  phase: "indexing",
128
- filesTotal: pending.length,
129
- filesIndexed: currentStats.filesIndexed + currentStats.filesFailed,
205
+ filesTotal: progressBase?.filesTotal ?? pending.length,
206
+ filesIndexed: (progressBase?.filesSucceeded ?? 0) +
207
+ currentStats.filesIndexed +
208
+ currentStats.filesFailed,
130
209
  filesFailed: currentStats.filesFailed,
131
210
  detail,
132
211
  embedding,
@@ -140,21 +219,45 @@ async function runIndexPass(ctx, report, scanningDetail, timings) {
140
219
  entitiesCreated: 0,
141
220
  });
142
221
  const stats = await indexFiles(pending, ctx, reportIndexing, timings);
222
+ throwIfIndexCancelled(ctx);
143
223
  return {
144
- filesScanned: scan.files.length,
224
+ filesScanned: scannedFiles.length,
145
225
  diff,
146
226
  stats,
147
227
  };
148
228
  }
149
- function reportIndexFinalizing(ctx, report, pass) {
229
+ function reportIndexFinalizing(ctx, report, pass, progressBase) {
150
230
  report({
151
231
  phase: "indexing",
232
+ filesTotal: progressBase?.filesTotal ??
233
+ pass.diff.added.length +
234
+ pass.diff.modified.length +
235
+ pass.diff.pending.length,
236
+ filesIndexed: (progressBase?.filesSucceeded ?? 0) +
237
+ pass.stats.filesIndexed +
238
+ pass.stats.filesFailed,
239
+ filesFailed: pass.stats.filesFailed,
240
+ detail: "finalizing index",
241
+ });
242
+ }
243
+ function retryProgressBase(pass) {
244
+ return {
245
+ filesSucceeded: pass.stats.filesIndexed,
152
246
  filesTotal: pass.diff.added.length +
153
247
  pass.diff.modified.length +
154
248
  pass.diff.pending.length,
155
- filesIndexed: pass.stats.filesIndexed + pass.stats.filesFailed,
156
- filesFailed: pass.stats.filesFailed,
157
- detail: "finalizing index",
249
+ };
250
+ }
251
+ function reportScanning(report, detail, progressBase) {
252
+ report({
253
+ phase: "scanning",
254
+ ...(progressBase
255
+ ? {
256
+ filesTotal: progressBase.filesTotal,
257
+ filesIndexed: progressBase.filesSucceeded,
258
+ }
259
+ : {}),
260
+ detail,
158
261
  });
159
262
  }
160
263
  function buildIndexResult(ctx, passes, durationMs, timings) {
@@ -162,8 +265,6 @@ function buildIndexResult(ctx, passes, durationMs, timings) {
162
265
  const retryPasses = passes.slice(1);
163
266
  const finalPass = passes[passes.length - 1];
164
267
  return {
165
- collectionId: ctx.collection.id,
166
- collectionName: ctx.collection.name,
167
268
  filesScanned: finalPass.filesScanned,
168
269
  filesAdded: firstPass.diff.added.length +
169
270
  retryPasses.reduce((count, pass) => count + pass.diff.added.length, 0),
@@ -180,20 +281,17 @@ function buildIndexResult(ctx, passes, durationMs, timings) {
180
281
  timings: timings.entries(),
181
282
  };
182
283
  }
183
- function optimizeStorage(ctx) {
284
+ async function optimizeStorage(ctx) {
184
285
  try {
185
- ctx.storage.optimize();
286
+ await ctx.storage.optimize();
186
287
  }
187
288
  catch (error) {
188
289
  throw toEngineError(error, "Indexing failed to finalize storage", {
189
290
  code: "ZVEC_GREP.ENGINE.INDEXING.OPTIMIZE_FAILED",
190
- context: collectionContext(ctx.collection),
291
+ context: workspaceIndexContext(ctx.workspaceIndex),
191
292
  });
192
293
  }
193
294
  }
194
- async function computeDiff(scannedFiles, storage) {
195
- return computeDiffFromFiles(scannedFiles, storage.listFiles());
196
- }
197
295
  async function computeDiffFromFiles(scannedFiles, existingFiles) {
198
296
  const existingById = new Map(existingFiles.map((file) => [file.id, file]));
199
297
  const seen = new Set();
@@ -242,10 +340,12 @@ async function indexFiles(files, ctx, onProgress, timings) {
242
340
  const embeddingScheduler = createEmbeddingScheduler(ctx.embeddingConcurrency, ctx.embeddingModel);
243
341
  const embeddingTiming = new ConcurrentTiming(timings, "index_embedding");
244
342
  const runningEmbeddings = new Set();
343
+ const chunkOptions = indexChunkOptions(ctx);
245
344
  const reportEmbeddingProgress = (currentStats, detail) => {
246
345
  onProgress(currentStats, detail, embeddingScheduler.snapshot());
247
346
  };
248
347
  const flushBatch = async () => {
348
+ throwIfIndexCancelled(ctx);
249
349
  if (batchFiles.length === 0) {
250
350
  return;
251
351
  }
@@ -254,6 +354,7 @@ async function indexFiles(files, ctx, onProgress, timings) {
254
354
  await scheduleEmbeddingTask(() => embedAndCommitBatch(filesToEmbed, ctx, stats, onProgress, embeddingScheduler, timings, embeddingTiming));
255
355
  };
256
356
  const scheduleEmbeddingTask = async (task) => {
357
+ throwIfIndexCancelled(ctx);
257
358
  const promise = task().finally(() => {
258
359
  runningEmbeddings.delete(promise);
259
360
  });
@@ -262,71 +363,109 @@ async function indexFiles(files, ctx, onProgress, timings) {
262
363
  await Promise.race(runningEmbeddings);
263
364
  }
264
365
  };
265
- for (const file of files) {
266
- onProgress(stats, `reading ${file.relativePath}`);
267
- const prepared = await timings.time("index_prepare", () => prepareFile(file, ctx));
268
- if ("failedReason" in prepared) {
269
- recordFileFailed(stats, file, prepared.failedReason);
270
- onProgress(stats, `failed ${file.relativePath}`);
271
- continue;
272
- }
273
- if (prepared.fragments.length === 0) {
274
- const committed = commitFile(prepared, [], ctx, stats);
275
- onProgress(stats, finishedFileDetail(committed, file.relativePath));
276
- continue;
277
- }
278
- if (prepared.fragments.length > ctx.embeddingModel.limits.maxBatchSize) {
279
- await flushBatch();
280
- await scheduleEmbeddingTask(async () => {
281
- reportEmbeddingProgress(stats, `embedding ${file.relativePath}`);
282
- await embedAndCommitFile(prepared, ctx, stats, onProgress, embeddingScheduler, timings, embeddingTiming);
283
- });
284
- continue;
285
- }
286
- if (batchFragmentCount > 0 &&
287
- batchFragmentCount + prepared.fragments.length >
288
- ctx.embeddingModel.limits.maxBatchSize) {
289
- await flushBatch();
290
- }
291
- batchFiles.push(prepared);
292
- batchFragmentCount += prepared.fragments.length;
293
- if (batchFragmentCount === ctx.embeddingModel.limits.maxBatchSize) {
294
- await flushBatch();
366
+ try {
367
+ for (const file of files) {
368
+ throwIfIndexCancelled(ctx);
369
+ onProgress(stats, `reading ${file.relativePath}`);
370
+ const prepared = await timings.time("index_prepare", () => prepareFile(file, ctx, chunkOptions));
371
+ throwIfIndexCancelled(ctx);
372
+ if ("failedReason" in prepared) {
373
+ recordFileFailed(stats, file, prepared.failedReason);
374
+ onProgress(stats, `failed ${file.relativePath}`);
375
+ continue;
376
+ }
377
+ if (prepared.fragments.length === 0) {
378
+ const committed = commitFile(prepared, [], ctx, stats);
379
+ onProgress(stats, finishedFileDetail(committed, file.relativePath));
380
+ continue;
381
+ }
382
+ if (prepared.fragments.length > ctx.embeddingModel.info.limits.maxBatchSize) {
383
+ await flushBatch();
384
+ await scheduleEmbeddingTask(async () => {
385
+ throwIfIndexCancelled(ctx);
386
+ reportEmbeddingProgress(stats, `embedding ${file.relativePath}`);
387
+ await embedAndCommitFile(prepared, ctx, stats, onProgress, embeddingScheduler, timings, embeddingTiming);
388
+ });
389
+ continue;
390
+ }
391
+ if (batchFragmentCount > 0 &&
392
+ batchFragmentCount + prepared.fragments.length >
393
+ ctx.embeddingModel.info.limits.maxBatchSize) {
394
+ await flushBatch();
395
+ }
396
+ batchFiles.push(prepared);
397
+ batchFragmentCount += prepared.fragments.length;
398
+ if (batchFragmentCount === ctx.embeddingModel.info.limits.maxBatchSize) {
399
+ await flushBatch();
400
+ }
295
401
  }
402
+ await flushBatch();
403
+ await Promise.all(runningEmbeddings);
404
+ }
405
+ catch (error) {
406
+ await Promise.allSettled(runningEmbeddings);
407
+ throw error;
296
408
  }
297
- await flushBatch();
298
- await Promise.all(runningEmbeddings);
409
+ throwIfIndexCancelled(ctx);
299
410
  return stats;
300
411
  }
301
- async function prepareFile(file, ctx) {
412
+ async function prepareFile(file, ctx, chunkOptions) {
302
413
  try {
414
+ throwIfIndexCancelled(ctx);
303
415
  const source = await readSource(file);
304
- const extracted = await new ExtractorRegistry().extract(source);
305
- const fragments = extracted.filter((fragment) => ctx.embeddingModel.supportedContentKinds.includes(fragment.content.kind));
416
+ const extracted = await extract(source, chunkOptions);
417
+ throwIfIndexCancelled(ctx);
418
+ const fragments = extracted.filter((fragment) => ctx.embeddingModel.info.inputKinds.includes(fragment.content.kind));
306
419
  return { file, fragments };
307
420
  }
308
421
  catch (error) {
422
+ if (indexIsCancelled(ctx)) {
423
+ throw indexCancellationError(ctx);
424
+ }
309
425
  return {
310
426
  file,
311
427
  failedReason: markFileFailed(ctx, file, error, "prepare"),
312
428
  };
313
429
  }
314
430
  }
431
+ function indexChunkOptions(ctx) {
432
+ const maxInputTokens = ctx.embeddingModel.info.limits.maxInputTokens;
433
+ if (maxInputTokens === undefined) {
434
+ return {};
435
+ }
436
+ const maxChunkChars = Math.floor(maxInputTokens * APPROXIMATE_CHARS_PER_TOKEN);
437
+ const chunkOverlapChars = Math.floor(maxChunkChars * CHUNK_OVERLAP_RATIO);
438
+ return { maxChunkChars, chunkOverlapChars };
439
+ }
315
440
  async function embedAndCommitBatch(files, ctx, stats, onProgress, embeddingScheduler, timings, embeddingTiming) {
316
441
  const fragments = files.flatMap((file) => file.fragments);
317
442
  try {
443
+ throwIfIndexCancelled(ctx);
318
444
  const contents = fragments.map(vectorContentForFragment);
319
445
  onProgress(stats, `embedding ${describePreparedFiles(files)}`, embeddingScheduler.snapshot());
320
- const vectors = await embeddingTiming.time(() => embedContentsWithRetry(contents, ctx.embeddingModel, embeddingScheduler));
446
+ const embedding = await embeddingTiming.time(() => embedContentsWithRetry(contents, ctx.embeddingModel, embeddingScheduler, ctx.signal, (progress) => reportModelDownloadProgress(stats, progress, onProgress, embeddingScheduler)));
447
+ const truncatedInputIndexes = new Set(embedding.truncated);
448
+ throwIfIndexCancelled(ctx);
321
449
  let offset = 0;
322
450
  for (const file of files) {
323
- const fileVectors = vectors.slice(offset, offset + file.fragments.length);
451
+ throwIfIndexCancelled(ctx);
452
+ const end = offset + file.fragments.length;
453
+ const fileVectors = embedding.vectors.slice(offset, end);
454
+ let truncatedFragmentCount = 0;
455
+ for (let index = offset; index < end; index++) {
456
+ if (truncatedInputIndexes.has(index)) {
457
+ truncatedFragmentCount++;
458
+ }
459
+ }
324
460
  offset += file.fragments.length;
325
- const committed = timings.timeSync("index_commit", () => commitFile(file, fileVectors, ctx, stats));
461
+ const committed = timings.timeSync("index_commit", () => commitFile(file, fileVectors, ctx, stats, truncatedFragmentCount));
326
462
  onProgress(stats, finishedFileDetail(committed, file.file.relativePath));
327
463
  }
328
464
  }
329
465
  catch (error) {
466
+ if (indexIsCancelled(ctx)) {
467
+ throw indexCancellationError(ctx);
468
+ }
330
469
  if (isRetryableEmbeddingError(error)) {
331
470
  for (const file of files) {
332
471
  const reason = markFileFailed(ctx, file.file, error, "embed");
@@ -343,29 +482,57 @@ async function embedAndCommitBatch(files, ctx, stats, onProgress, embeddingSched
343
482
  }
344
483
  async function embedAndCommitFile(file, ctx, stats, onProgress, embeddingScheduler, timings, embeddingTiming) {
345
484
  try {
346
- const vectors = await embeddingTiming.time(() => embedFragments(file.fragments, ctx.embeddingModel, embeddingScheduler));
347
- const committed = timings.timeSync("index_commit", () => commitFile(file, vectors, ctx, stats));
485
+ throwIfIndexCancelled(ctx);
486
+ const embedding = await embeddingTiming.time(() => embedFragments(file.fragments, ctx.embeddingModel, embeddingScheduler, ctx.signal, (progress) => reportModelDownloadProgress(stats, progress, onProgress, embeddingScheduler)));
487
+ throwIfIndexCancelled(ctx);
488
+ const committed = timings.timeSync("index_commit", () => commitFile(file, embedding.vectors, ctx, stats, embedding.truncated.length));
348
489
  onProgress(stats, finishedFileDetail(committed, file.file.relativePath));
349
490
  }
350
491
  catch (error) {
492
+ if (indexIsCancelled(ctx)) {
493
+ throw indexCancellationError(ctx);
494
+ }
351
495
  const reason = markFileFailed(ctx, file.file, error, "embed");
352
496
  recordFileFailed(stats, file.file, reason);
353
497
  onProgress(stats, finishedFileDetail(false, file.file.relativePath));
354
498
  }
355
499
  }
356
- function commitFile(file, vectors, ctx, stats) {
500
+ function commitFile(file, vectors, ctx, stats, truncatedFragmentCount = 0) {
357
501
  try {
358
- ctx.storage.upsertFile(file.file, file.fragments, vectors);
502
+ throwIfIndexCancelled(ctx);
503
+ ctx.storage.upsertFile(file.file, file.fragments, vectors, {
504
+ truncatedFragmentCount,
505
+ });
359
506
  stats.filesIndexed++;
360
507
  stats.entitiesCreated += countPublicEntities(file.fragments);
361
508
  return true;
362
509
  }
363
510
  catch (error) {
511
+ if (indexIsCancelled(ctx)) {
512
+ throw indexCancellationError(ctx);
513
+ }
364
514
  const reason = markFileFailed(ctx, file.file, error, "commit");
365
515
  recordFileFailed(stats, file.file, reason);
366
516
  return false;
367
517
  }
368
518
  }
519
+ function throwIfIndexCancelled(ctx) {
520
+ if (!indexIsCancelled(ctx)) {
521
+ return;
522
+ }
523
+ throw indexCancellationError(ctx);
524
+ }
525
+ function indexIsCancelled(ctx) {
526
+ return ctx.signal?.aborted === true;
527
+ }
528
+ function indexCancellationError(ctx) {
529
+ return ctx.signal?.reason instanceof Error
530
+ ? ctx.signal.reason
531
+ : new EngineError("Indexing was cancelled.", {
532
+ code: "ZVEC_GREP.ENGINE.INDEXING.CANCELLED",
533
+ context: workspaceIndexContext(ctx.workspaceIndex),
534
+ });
535
+ }
369
536
  function recordFileFailed(stats, file, reason) {
370
537
  stats.filesFailed++;
371
538
  stats.failedFiles.push(file.relativePath);
@@ -417,14 +584,15 @@ async function readSource(file) {
417
584
  text: bytes.toString("utf8"),
418
585
  };
419
586
  }
420
- async function embedFragments(fragments, model, embeddingScheduler) {
587
+ async function embedFragments(fragments, model, embeddingScheduler, signal, onModelProgress) {
421
588
  const batches = [];
422
- for (let start = 0; start < fragments.length; start += model.limits.maxBatchSize) {
423
- const batch = fragments.slice(start, start + model.limits.maxBatchSize);
589
+ for (let start = 0; start < fragments.length; start += model.info.limits.maxBatchSize) {
590
+ const batch = fragments.slice(start, start + model.info.limits.maxBatchSize);
424
591
  batches.push({ start, fragments: batch });
425
592
  }
426
593
  const vectors = new Array(fragments.length);
427
- const results = await Promise.allSettled(batches.map((batch) => embedFragmentBatch(batch.fragments, model, batch.start, embeddingScheduler)));
594
+ const truncatedInputIndexes = [];
595
+ const results = await Promise.allSettled(batches.map((batch) => embedFragmentBatch(batch.fragments, model, batch.start, embeddingScheduler, signal, onModelProgress)));
428
596
  let firstError;
429
597
  for (const [index, result] of results.entries()) {
430
598
  if (result.status === "rejected") {
@@ -432,43 +600,54 @@ async function embedFragments(fragments, model, embeddingScheduler) {
432
600
  continue;
433
601
  }
434
602
  const start = batches[index].start;
435
- for (const [offset, vector] of result.value.entries()) {
603
+ for (const [offset, vector] of result.value.vectors.entries()) {
436
604
  vectors[start + offset] = vector;
437
605
  }
606
+ truncatedInputIndexes.push(...result.value.truncated.map((inputIndex) => start + inputIndex));
438
607
  }
439
608
  if (firstError) {
440
609
  throw firstError;
441
610
  }
442
- return vectors;
611
+ return {
612
+ vectors,
613
+ truncated: truncatedInputIndexes,
614
+ };
443
615
  }
444
- async function embedFragmentBatch(fragments, model, startIndex, embeddingScheduler) {
616
+ async function embedFragmentBatch(fragments, model, startIndex, embeddingScheduler, signal, onModelProgress) {
445
617
  const contents = fragments.map(vectorContentForFragment);
446
618
  try {
447
- return await embedContentsWithRetry(contents, model, embeddingScheduler);
619
+ return await embedContentsWithRetry(contents, model, embeddingScheduler, signal, onModelProgress);
448
620
  }
449
621
  catch (error) {
450
622
  if (fragments.length === 1 || isRetryableEmbeddingError(error)) {
451
623
  throw error;
452
624
  }
453
- return await embedFragmentBatchOneByOne(fragments, model, startIndex, embeddingScheduler);
625
+ return await embedFragmentBatchOneByOne(fragments, model, startIndex, embeddingScheduler, signal, onModelProgress);
454
626
  }
455
627
  }
456
- async function embedFragmentBatchOneByOne(fragments, model, startIndex, embeddingScheduler) {
628
+ async function embedFragmentBatchOneByOne(fragments, model, startIndex, embeddingScheduler, signal, onModelProgress) {
457
629
  const vectors = [];
630
+ const truncatedInputIndexes = [];
458
631
  for (const [index, fragment] of fragments.entries()) {
459
632
  try {
460
- const [vector] = await embedContentsWithRetry([vectorContentForFragment(fragment)], model, embeddingScheduler);
461
- vectors.push(vector);
633
+ const result = await embedContentsWithRetry([vectorContentForFragment(fragment)], model, embeddingScheduler, signal, onModelProgress);
634
+ vectors.push(result.vectors[0]);
635
+ if (result.truncated.length > 0) {
636
+ truncatedInputIndexes.push(index);
637
+ }
462
638
  }
463
639
  catch (error) {
464
640
  throw new EngineError("Embedding entity fragment failed after one-by-one fallback", {
465
641
  code: "ZVEC_GREP.ENGINE.INDEXING.EMBEDDING_FRAGMENT_FAILED",
466
- context: `model=${model.ref.model} fragmentId=${fragment.id} fragmentIndex=${startIndex + index}`,
642
+ context: `model=${model.info.reference} fragmentId=${fragment.id} fragmentIndex=${startIndex + index}`,
467
643
  cause: error,
468
644
  });
469
645
  }
470
646
  }
471
- return vectors;
647
+ return {
648
+ vectors,
649
+ truncated: truncatedInputIndexes,
650
+ };
472
651
  }
473
652
  function vectorContentForFragment(fragment) {
474
653
  if (fragment.content.kind !== "text") {
@@ -513,13 +692,18 @@ function compactMetadataLines(lines) {
513
692
  .filter((line) => line !== null && line.trim().length > 0)
514
693
  .join("\n");
515
694
  }
516
- async function embedContentsWithRetry(contents, model, embeddingScheduler) {
695
+ async function embedContentsWithRetry(contents, model, embeddingScheduler, signal, onModelProgress) {
517
696
  let attempt = 0;
518
697
  while (true) {
519
698
  let retry = nonRetryableEmbeddingError();
520
699
  let delayMs = 0;
521
700
  try {
522
- const vectors = await embeddingScheduler.run(() => model.embed(contents, { purpose: "document" }), (error) => {
701
+ throwIfAborted(signal);
702
+ const result = await embeddingScheduler.run(() => model.embed(contents, {
703
+ purpose: "document",
704
+ signal,
705
+ onProgress: onModelProgress,
706
+ }), signal, (error) => {
523
707
  retry = classifyEmbeddingRetry(error);
524
708
  delayMs = retryDelayMs(attempt, retry);
525
709
  if (retry.retryable) {
@@ -530,9 +714,14 @@ async function embedContentsWithRetry(contents, model, embeddingScheduler) {
530
714
  }
531
715
  });
532
716
  embeddingScheduler.recordSuccess();
533
- return vectors;
717
+ return result;
534
718
  }
535
719
  catch (error) {
720
+ if (signal?.aborted) {
721
+ throw signal.reason instanceof Error
722
+ ? signal.reason
723
+ : new Error("Embedding was cancelled.");
724
+ }
536
725
  if (!retry.retryable) {
537
726
  retry = classifyEmbeddingRetry(error);
538
727
  delayMs = retryDelayMs(attempt, retry);
@@ -540,11 +729,17 @@ async function embedContentsWithRetry(contents, model, embeddingScheduler) {
540
729
  if (attempt >= maxRetryAttempts(retry) || !retry.retryable) {
541
730
  throw error;
542
731
  }
543
- await delay(delayMs);
732
+ await abortableDelay(delayMs, signal);
544
733
  attempt++;
545
734
  }
546
735
  }
547
736
  }
737
+ function reportModelDownloadProgress(stats, progress, onProgress, embeddingScheduler) {
738
+ onProgress(stats, `downloading ${progress.model}`, {
739
+ ...embeddingScheduler.snapshot(),
740
+ ...progress,
741
+ });
742
+ }
548
743
  function createEmbeddingScheduler(requestedConcurrency, model) {
549
744
  return new AdaptiveEmbeddingScheduler(resolveEmbeddingConcurrencyPolicy(requestedConcurrency, model));
550
745
  }
@@ -559,10 +754,11 @@ function resolveEmbeddingConcurrencyPolicy(requestedConcurrency, model) {
559
754
  adaptive: requestedConcurrency > 1,
560
755
  };
561
756
  }
562
- const initial = model.recommendedIndexConcurrency ?? DEFAULT_EMBEDDING_CONCURRENCY;
563
- const max = model.maxIndexConcurrency ??
564
- (initial <= 1 ? initial : DEFAULT_EMBEDDING_MAX_CONCURRENCY);
565
- const min = Math.min(initial, DEFAULT_EMBEDDING_MIN_CONCURRENCY);
757
+ const remote = model.info.provider === "qwen";
758
+ const multimodal = model.info.inputKinds.includes("image");
759
+ const initial = remote ? (multimodal ? 4 : 8) : 1;
760
+ const max = remote ? (multimodal ? 8 : 12) : 1;
761
+ const min = Math.min(initial, 4);
566
762
  return {
567
763
  initial,
568
764
  min,
@@ -585,11 +781,13 @@ class AdaptiveEmbeddingScheduler {
585
781
  get taskConcurrency() {
586
782
  return this.policy.max;
587
783
  }
588
- async run(task, onError) {
589
- await this.waitForCooldown();
590
- await this.acquire();
784
+ async run(task, signal, onError) {
785
+ throwIfAborted(signal);
786
+ await this.waitForCooldown(signal);
787
+ await this.acquire(signal);
591
788
  try {
592
- await this.waitForCooldown();
789
+ await this.waitForCooldown(signal);
790
+ throwIfAborted(signal);
593
791
  return await task();
594
792
  }
595
793
  catch (error) {
@@ -631,19 +829,36 @@ class AdaptiveEmbeddingScheduler {
631
829
  retryableFailures: this.retryableFailures,
632
830
  };
633
831
  }
634
- async waitForCooldown() {
832
+ async waitForCooldown(signal) {
635
833
  const remaining = this.cooldownUntil - Date.now();
636
834
  if (remaining > 0) {
637
- await delay(remaining);
835
+ await abortableDelay(remaining, signal);
638
836
  }
639
837
  }
640
- async acquire() {
838
+ async acquire(signal) {
839
+ throwIfAborted(signal);
641
840
  if (this.active < this.currentConcurrency) {
642
841
  this.active++;
643
842
  return;
644
843
  }
645
- await new Promise((resolve) => {
646
- this.queue.push(resolve);
844
+ await new Promise((resolve, reject) => {
845
+ const queued = () => {
846
+ signal?.removeEventListener("abort", abort);
847
+ resolve();
848
+ };
849
+ const abort = () => {
850
+ const index = this.queue.indexOf(queued);
851
+ if (index >= 0) {
852
+ this.queue.splice(index, 1);
853
+ }
854
+ reject(abortError(signal));
855
+ };
856
+ if (signal?.aborted) {
857
+ reject(abortError(signal));
858
+ return;
859
+ }
860
+ signal?.addEventListener("abort", abort, { once: true });
861
+ this.queue.push(queued);
647
862
  });
648
863
  }
649
864
  release() {
@@ -724,8 +939,32 @@ function retryAfterMsFromText(text) {
724
939
  }
725
940
  return undefined;
726
941
  }
727
- function delay(ms) {
728
- return new Promise((resolve) => setTimeout(resolve, ms));
942
+ function abortableDelay(ms, signal) {
943
+ if (ms <= 0) {
944
+ return Promise.resolve();
945
+ }
946
+ throwIfAborted(signal);
947
+ return new Promise((resolve, reject) => {
948
+ const timer = setTimeout(() => {
949
+ signal?.removeEventListener("abort", abort);
950
+ resolve();
951
+ }, ms);
952
+ const abort = () => {
953
+ clearTimeout(timer);
954
+ reject(abortError(signal));
955
+ };
956
+ signal?.addEventListener("abort", abort, { once: true });
957
+ });
958
+ }
959
+ function throwIfAborted(signal) {
960
+ if (signal?.aborted) {
961
+ throw abortError(signal);
962
+ }
963
+ }
964
+ function abortError(signal) {
965
+ return signal?.reason instanceof Error
966
+ ? signal.reason
967
+ : new Error("Embedding was cancelled.");
729
968
  }
730
969
  function countPublicEntities(fragments) {
731
970
  let count = 0;
@@ -777,10 +1016,10 @@ function toEngineError(error, message, options) {
777
1016
  cause: error,
778
1017
  });
779
1018
  }
780
- function collectionContext(collection) {
1019
+ function workspaceIndexContext(workspaceIndex) {
781
1020
  return (errorDetails([
782
- detail("collection_id", collection.id),
783
- collectionDetail(collection.name),
1021
+ detail("workspace_index_id", workspaceIndex.id),
1022
+ workspaceIndexDetail(workspaceIndex.name),
784
1023
  ]) ?? "");
785
1024
  }
786
1025
  function fileContext(file) {