@mastra/core 1.31.0-alpha.3 → 1.31.0-alpha.5

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 (144) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/agent/durable/index.cjs +22 -22
  3. package/dist/agent/durable/index.js +4 -4
  4. package/dist/agent/index.cjs +9 -9
  5. package/dist/agent/index.js +1 -1
  6. package/dist/browser/index.cjs +5 -5
  7. package/dist/browser/index.js +2 -2
  8. package/dist/channels/index.cjs +4 -4
  9. package/dist/channels/index.js +1 -1
  10. package/dist/{chunk-LEXSJDD5.cjs → chunk-3CBQ4FAZ.cjs} +215 -18
  11. package/dist/chunk-3CBQ4FAZ.cjs.map +1 -0
  12. package/dist/{chunk-EMLA7DXA.cjs → chunk-4RFGOX6E.cjs} +9 -9
  13. package/dist/{chunk-EMLA7DXA.cjs.map → chunk-4RFGOX6E.cjs.map} +1 -1
  14. package/dist/{chunk-P5XCYDW7.js → chunk-52IAV52S.js} +3 -3
  15. package/dist/{chunk-P5XCYDW7.js.map → chunk-52IAV52S.js.map} +1 -1
  16. package/dist/{chunk-OQ6ORZLY.cjs → chunk-5ANWVE3P.cjs} +216 -35
  17. package/dist/chunk-5ANWVE3P.cjs.map +1 -0
  18. package/dist/{chunk-HBCHXSQT.js → chunk-5AZE6Y7E.js} +6 -6
  19. package/dist/{chunk-HBCHXSQT.js.map → chunk-5AZE6Y7E.js.map} +1 -1
  20. package/dist/{chunk-JXWLABNY.cjs → chunk-ALZAXC3J.cjs} +19 -19
  21. package/dist/{chunk-JXWLABNY.cjs.map → chunk-ALZAXC3J.cjs.map} +1 -1
  22. package/dist/{chunk-IKXZNMTB.cjs → chunk-ATJ2RA5L.cjs} +5 -5
  23. package/dist/{chunk-IKXZNMTB.cjs.map → chunk-ATJ2RA5L.cjs.map} +1 -1
  24. package/dist/{chunk-BEDXCJ3D.js → chunk-D6WNIFWU.js} +4 -4
  25. package/dist/{chunk-BEDXCJ3D.js.map → chunk-D6WNIFWU.js.map} +1 -1
  26. package/dist/{chunk-C6KGFSLD.js → chunk-D7ORPD2O.js} +3 -3
  27. package/dist/{chunk-C6KGFSLD.js.map → chunk-D7ORPD2O.js.map} +1 -1
  28. package/dist/{chunk-WYDQEW3F.cjs → chunk-DZBT3XFM.cjs} +7 -7
  29. package/dist/{chunk-WYDQEW3F.cjs.map → chunk-DZBT3XFM.cjs.map} +1 -1
  30. package/dist/{chunk-3MJ2YZKW.js → chunk-EZANPKCM.js} +3 -3
  31. package/dist/{chunk-3MJ2YZKW.js.map → chunk-EZANPKCM.js.map} +1 -1
  32. package/dist/{chunk-5AFU6MDJ.js → chunk-F4PWGVHW.js} +212 -32
  33. package/dist/chunk-F4PWGVHW.js.map +1 -0
  34. package/dist/{chunk-N3MFQ5IC.cjs → chunk-GYK5WRWE.cjs} +3 -3
  35. package/dist/{chunk-N3MFQ5IC.cjs.map → chunk-GYK5WRWE.cjs.map} +1 -1
  36. package/dist/{chunk-FBSDOK7N.cjs → chunk-HDZV5R52.cjs} +224 -224
  37. package/dist/{chunk-FBSDOK7N.cjs.map → chunk-HDZV5R52.cjs.map} +1 -1
  38. package/dist/{chunk-ORGS37CO.js → chunk-IB3UPSY2.js} +9 -9
  39. package/dist/{chunk-ORGS37CO.js.map → chunk-IB3UPSY2.js.map} +1 -1
  40. package/dist/{chunk-ZTVCTOC4.js → chunk-II2JGWWS.js} +3 -3
  41. package/dist/{chunk-ZTVCTOC4.js.map → chunk-II2JGWWS.js.map} +1 -1
  42. package/dist/{chunk-QYT6OF4I.cjs → chunk-O4NY7VXW.cjs} +17 -17
  43. package/dist/{chunk-QYT6OF4I.cjs.map → chunk-O4NY7VXW.cjs.map} +1 -1
  44. package/dist/{chunk-BICHLSKY.cjs → chunk-PLCMSZZR.cjs} +20 -20
  45. package/dist/{chunk-BICHLSKY.cjs.map → chunk-PLCMSZZR.cjs.map} +1 -1
  46. package/dist/{chunk-U7SCBEKL.js → chunk-PW5TFXZI.js} +3 -3
  47. package/dist/{chunk-U7SCBEKL.js.map → chunk-PW5TFXZI.js.map} +1 -1
  48. package/dist/{chunk-6YTX2D43.cjs → chunk-QB6SFTJF.cjs} +57 -57
  49. package/dist/{chunk-6YTX2D43.cjs.map → chunk-QB6SFTJF.cjs.map} +1 -1
  50. package/dist/{chunk-7CDGKPLZ.cjs → chunk-U7C3WU7B.cjs} +389 -389
  51. package/dist/{chunk-7CDGKPLZ.cjs.map → chunk-U7C3WU7B.cjs.map} +1 -1
  52. package/dist/{chunk-ENFUF5ZE.js → chunk-URCXU22J.js} +8 -8
  53. package/dist/{chunk-ENFUF5ZE.js.map → chunk-URCXU22J.js.map} +1 -1
  54. package/dist/{chunk-SE2NIAOC.js → chunk-X735G7KE.js} +4 -4
  55. package/dist/{chunk-SE2NIAOC.js.map → chunk-X735G7KE.js.map} +1 -1
  56. package/dist/{chunk-6BMV3INL.js → chunk-ZKHNSFUH.js} +213 -16
  57. package/dist/chunk-ZKHNSFUH.js.map +1 -0
  58. package/dist/datasets/experiment/index.d.ts.map +1 -1
  59. package/dist/datasets/experiment/scorer.d.ts +25 -0
  60. package/dist/datasets/experiment/scorer.d.ts.map +1 -1
  61. package/dist/datasets/experiment/types.d.ts +18 -2
  62. package/dist/datasets/experiment/types.d.ts.map +1 -1
  63. package/dist/datasets/index.cjs +11 -11
  64. package/dist/datasets/index.js +1 -1
  65. package/dist/docs/SKILL.md +1 -1
  66. package/dist/docs/assets/SOURCE_MAP.json +140 -140
  67. package/dist/docs/references/docs-agents-adding-voice.md +14 -13
  68. package/dist/docs/references/docs-voice-overview.md +66 -0
  69. package/dist/docs/references/docs-voice-speech-to-speech.md +45 -1
  70. package/dist/docs/references/docs-workspace-search.md +39 -0
  71. package/dist/docs/references/reference-datasets-startExperiment.md +28 -2
  72. package/dist/docs/references/reference-workspace-workspace-class.md +1 -1
  73. package/dist/docs/references/reference.md +2 -0
  74. package/dist/evals/index.cjs +6 -6
  75. package/dist/evals/index.js +2 -2
  76. package/dist/evals/scoreTraces/index.cjs +3 -3
  77. package/dist/evals/scoreTraces/index.js +1 -1
  78. package/dist/harness/index.cjs +11 -11
  79. package/dist/harness/index.js +6 -6
  80. package/dist/index.cjs +2 -2
  81. package/dist/index.js +1 -1
  82. package/dist/llm/index.cjs +20 -20
  83. package/dist/llm/index.js +5 -5
  84. package/dist/loop/index.cjs +14 -14
  85. package/dist/loop/index.js +1 -1
  86. package/dist/mastra/index.cjs +2 -2
  87. package/dist/mastra/index.js +1 -1
  88. package/dist/mastra-OCULSBP5.js +3 -0
  89. package/dist/{mastra-ZMGSDAUN.js.map → mastra-OCULSBP5.js.map} +1 -1
  90. package/dist/mastra-VMPA5UVF.cjs +12 -0
  91. package/dist/{mastra-P7T3UKV5.cjs.map → mastra-VMPA5UVF.cjs.map} +1 -1
  92. package/dist/memory/index.cjs +19 -19
  93. package/dist/memory/index.js +1 -1
  94. package/dist/models-dev-CCJECLMT.cjs +12 -0
  95. package/dist/{models-dev-GJQCRHM3.cjs.map → models-dev-CCJECLMT.cjs.map} +1 -1
  96. package/dist/models-dev-F6OUM7S7.js +3 -0
  97. package/dist/{models-dev-DL6NWDNP.js.map → models-dev-F6OUM7S7.js.map} +1 -1
  98. package/dist/netlify-2YTGXPQW.js +3 -0
  99. package/dist/{netlify-3CKP3DSR.js.map → netlify-2YTGXPQW.js.map} +1 -1
  100. package/dist/netlify-4NR3G6EJ.cjs +12 -0
  101. package/dist/{netlify-O34AZQ5R.cjs.map → netlify-4NR3G6EJ.cjs.map} +1 -1
  102. package/dist/processor-provider/index.cjs +10 -10
  103. package/dist/processor-provider/index.js +1 -1
  104. package/dist/processors/index.cjs +49 -49
  105. package/dist/processors/index.js +1 -1
  106. package/dist/provider-registry-HZHKE2KN.cjs +44 -0
  107. package/dist/{provider-registry-DI3SQCLD.cjs.map → provider-registry-HZHKE2KN.cjs.map} +1 -1
  108. package/dist/provider-registry-QREDL7WJ.js +3 -0
  109. package/dist/{provider-registry-ZTTSZTTB.js.map → provider-registry-QREDL7WJ.js.map} +1 -1
  110. package/dist/relevance/index.cjs +3 -3
  111. package/dist/relevance/index.js +1 -1
  112. package/dist/runner-FOG455RH.js +3 -0
  113. package/dist/{runner-BIOR2SMR.js.map → runner-FOG455RH.js.map} +1 -1
  114. package/dist/runner-PDJYD3PQ.cjs +16 -0
  115. package/dist/{runner-J24LBLPO.cjs.map → runner-PDJYD3PQ.cjs.map} +1 -1
  116. package/dist/stream/index.cjs +11 -11
  117. package/dist/stream/index.js +1 -1
  118. package/dist/tool-loop-agent/index.cjs +4 -4
  119. package/dist/tool-loop-agent/index.js +1 -1
  120. package/dist/workflows/evented/index.cjs +10 -10
  121. package/dist/workflows/evented/index.js +1 -1
  122. package/dist/workflows/index.cjs +24 -24
  123. package/dist/workflows/index.js +1 -1
  124. package/dist/workspace/index.cjs +68 -68
  125. package/dist/workspace/index.js +1 -1
  126. package/dist/workspace/search/search-engine.d.ts +67 -4
  127. package/dist/workspace/search/search-engine.d.ts.map +1 -1
  128. package/dist/workspace/workspace.d.ts +14 -0
  129. package/dist/workspace/workspace.d.ts.map +1 -1
  130. package/package.json +5 -5
  131. package/dist/chunk-5AFU6MDJ.js.map +0 -1
  132. package/dist/chunk-6BMV3INL.js.map +0 -1
  133. package/dist/chunk-LEXSJDD5.cjs.map +0 -1
  134. package/dist/chunk-OQ6ORZLY.cjs.map +0 -1
  135. package/dist/mastra-P7T3UKV5.cjs +0 -12
  136. package/dist/mastra-ZMGSDAUN.js +0 -3
  137. package/dist/models-dev-DL6NWDNP.js +0 -3
  138. package/dist/models-dev-GJQCRHM3.cjs +0 -12
  139. package/dist/netlify-3CKP3DSR.js +0 -3
  140. package/dist/netlify-O34AZQ5R.cjs +0 -12
  141. package/dist/provider-registry-DI3SQCLD.cjs +0 -44
  142. package/dist/provider-registry-ZTTSZTTB.js +0 -3
  143. package/dist/runner-BIOR2SMR.js +0 -3
  144. package/dist/runner-J24LBLPO.cjs +0 -16
@@ -5,6 +5,7 @@ var chunkKF4FCWWL_cjs = require('./chunk-KF4FCWWL.cjs');
5
5
  var chunkFCQNDFEW_cjs = require('./chunk-FCQNDFEW.cjs');
6
6
  var chunk7GW2TQXP_cjs = require('./chunk-7GW2TQXP.cjs');
7
7
  var nodePath = require('path');
8
+ var pMap = require('p-map');
8
9
  var posixPath = require('path/posix');
9
10
  var fs = require('fs');
10
11
  var fs2 = require('fs/promises');
@@ -41,6 +42,7 @@ function _interopNamespace(e) {
41
42
  }
42
43
 
43
44
  var nodePath__namespace = /*#__PURE__*/_interopNamespace(nodePath);
45
+ var pMap__default = /*#__PURE__*/_interopDefault(pMap);
44
46
  var posixPath__default = /*#__PURE__*/_interopDefault(posixPath);
45
47
  var fs2__namespace = /*#__PURE__*/_interopNamespace(fs2);
46
48
  var os3__namespace = /*#__PURE__*/_interopNamespace(os3);
@@ -1658,7 +1660,7 @@ function isLSPAvailable() {
1658
1660
  return jsonrpcModule !== null;
1659
1661
  }
1660
1662
  try {
1661
- const req = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-OQ6ORZLY.cjs', document.baseURI).href)));
1663
+ const req = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-5ANWVE3P.cjs', document.baseURI).href)));
1662
1664
  req.resolve("vscode-jsonrpc/node");
1663
1665
  req.resolve("vscode-languageserver-protocol");
1664
1666
  return true;
@@ -1672,7 +1674,7 @@ async function loadLSPDeps() {
1672
1674
  return { ...jsonrpcModule, ...lspProtocolModule };
1673
1675
  }
1674
1676
  try {
1675
- const req = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-OQ6ORZLY.cjs', document.baseURI).href)));
1677
+ const req = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-5ANWVE3P.cjs', document.baseURI).href)));
1676
1678
  const jsonrpc = req("vscode-jsonrpc/node");
1677
1679
  const protocol = req("vscode-languageserver-protocol");
1678
1680
  jsonrpcModule = {
@@ -4666,8 +4668,10 @@ var BM25Index = class _BM25Index {
4666
4668
  return idf * (numerator / denominator);
4667
4669
  }
4668
4670
  };
4669
-
4670
- // src/workspace/search/search-engine.ts
4671
+ function isBatchEmbedder(embedder) {
4672
+ return typeof embedder === "function" && embedder.batch === true;
4673
+ }
4674
+ var DEFAULT_INDEX_MANY_CONCURRENCY = 8;
4671
4675
  var DEFAULT_MAX_CHUNK_CHARS = 4e3;
4672
4676
  var DEFAULT_OVERLAP_LINES = 3;
4673
4677
  function splitIntoChunks(text, options = {}) {
@@ -4759,12 +4763,15 @@ var SearchEngine = class {
4759
4763
  }
4760
4764
  }
4761
4765
  /**
4762
- * Index multiple documents
4766
+ * Index multiple documents (up to `concurrency` at a time when async vector work runs).
4767
+ *
4768
+ * @param docs - Documents to index
4769
+ * @param options - `p-map` options; `concurrency` defaults to 8
4763
4770
  */
4764
- async indexMany(docs) {
4765
- for (const doc of docs) {
4766
- await this.index(doc);
4767
- }
4771
+ async indexMany(docs, options) {
4772
+ const stopOnError = options?.stopOnError;
4773
+ const concurrency = options?.concurrency ?? DEFAULT_INDEX_MANY_CONCURRENCY;
4774
+ await pMap__default.default(docs, (doc) => this.index(doc), { stopOnError, concurrency });
4768
4775
  }
4769
4776
  /**
4770
4777
  * Remove a document from the index
@@ -4915,12 +4922,63 @@ var SearchEngine = class {
4915
4922
  throw new Error("No search configuration available. Provide bm25 or vector config.");
4916
4923
  }
4917
4924
  /**
4918
- * Index a single document in the vector store
4925
+ * Embed a single text, dispatching to the batch path with a one-element array
4926
+ * when the configured embedder is batch-capable.
4927
+ */
4928
+ async #embedOne(text) {
4929
+ if (!this.#vectorConfig) {
4930
+ throw new Error("Vector configuration is required to embed text.");
4931
+ }
4932
+ const { embedder } = this.#vectorConfig;
4933
+ if (isBatchEmbedder(embedder)) {
4934
+ const [embedding] = await embedder([text]);
4935
+ if (!embedding) {
4936
+ throw new Error("Batch embedder returned no embedding for input text.");
4937
+ }
4938
+ return embedding;
4939
+ }
4940
+ return embedder(text);
4941
+ }
4942
+ /**
4943
+ * Embed many texts. Uses a single batched call (chunked by `maxBatchSize`)
4944
+ * when the embedder is batch-capable; otherwise falls back to parallel
4945
+ * single-text calls.
4946
+ */
4947
+ async #embedAll(texts) {
4948
+ if (!this.#vectorConfig) {
4949
+ throw new Error("Vector configuration is required to embed texts.");
4950
+ }
4951
+ if (texts.length === 0) return [];
4952
+ const { embedder } = this.#vectorConfig;
4953
+ if (isBatchEmbedder(embedder)) {
4954
+ const max = embedder.maxBatchSize;
4955
+ if (max === void 0 || texts.length <= max) {
4956
+ return embedder(texts);
4957
+ }
4958
+ const chunks = [];
4959
+ for (let i = 0; i < texts.length; i += max) {
4960
+ chunks.push(texts.slice(i, i + max));
4961
+ }
4962
+ const results = await pMap__default.default(chunks, (chunk) => embedder(chunk), {
4963
+ concurrency: DEFAULT_INDEX_MANY_CONCURRENCY
4964
+ });
4965
+ return results.flat();
4966
+ }
4967
+ return pMap__default.default(texts, (text) => embedder(text), {
4968
+ concurrency: DEFAULT_INDEX_MANY_CONCURRENCY
4969
+ });
4970
+ }
4971
+ /**
4972
+ * Index a single document in the vector store.
4973
+ *
4974
+ * Used by the eager (non-lazy) write path. The lazy flush path embeds all
4975
+ * pending docs together via {@link SearchEngine.#flushVectorBatch} for true
4976
+ * batch embedding when supported.
4919
4977
  */
4920
4978
  async #indexVector(doc) {
4921
4979
  if (!this.#vectorConfig) return;
4922
- const { vectorStore, embedder, indexName } = this.#vectorConfig;
4923
- const embedding = await embedder(doc.content);
4980
+ const { vectorStore, indexName } = this.#vectorConfig;
4981
+ const embedding = await this.#embedOne(doc.content);
4924
4982
  if (!this.#vectorIndexReady) {
4925
4983
  try {
4926
4984
  await vectorStore.createIndex({ indexName, dimension: embedding.length });
@@ -4942,16 +5000,80 @@ var SearchEngine = class {
4942
5000
  this.#vectorIndexReady = true;
4943
5001
  }
4944
5002
  /**
4945
- * Ensure vector index is built (for lazy mode)
5003
+ * Embed and upsert a batch of documents in as few provider calls as possible.
5004
+ *
5005
+ * - If the embedder is batch-capable, all texts go through a single embedder
5006
+ * call (chunked by `maxBatchSize`), then a single `upsert` with all vectors.
5007
+ * - Otherwise falls back to per-doc embedding via {@link SearchEngine.#indexVector}.
5008
+ */
5009
+ async #flushVectorBatch(docs) {
5010
+ if (!this.#vectorConfig || docs.length === 0) return;
5011
+ const { vectorStore, embedder, indexName } = this.#vectorConfig;
5012
+ if (!isBatchEmbedder(embedder)) {
5013
+ await pMap__default.default(docs, (doc) => this.#indexVector(doc), {
5014
+ concurrency: DEFAULT_INDEX_MANY_CONCURRENCY
5015
+ });
5016
+ return;
5017
+ }
5018
+ const embeddings = await this.#embedAll(docs.map((d) => d.content));
5019
+ if (embeddings.length !== docs.length) {
5020
+ throw new Error(`Batch embedder returned ${embeddings.length} embeddings for ${docs.length} inputs.`);
5021
+ }
5022
+ if (!this.#vectorIndexReady) {
5023
+ const dim = embeddings[0].length;
5024
+ try {
5025
+ await vectorStore.createIndex({ indexName, dimension: dim });
5026
+ } catch {
5027
+ }
5028
+ }
5029
+ await vectorStore.upsert({
5030
+ indexName,
5031
+ vectors: embeddings,
5032
+ metadata: docs.map((doc) => ({
5033
+ id: doc.id,
5034
+ text: doc.content,
5035
+ ...doc.metadata
5036
+ })),
5037
+ ids: docs.map((d) => d.id)
5038
+ });
5039
+ this.#vectorIndexReady = true;
5040
+ }
5041
+ /**
5042
+ * Collapse duplicate document ids so a single deterministic upsert runs per id (last queue entry wins).
5043
+ */
5044
+ #dedupePendingVectorDocsLastWins(docs) {
5045
+ const byId = /* @__PURE__ */ new Map();
5046
+ for (const doc of docs) {
5047
+ byId.set(doc.id, doc);
5048
+ }
5049
+ return [...byId.values()];
5050
+ }
5051
+ /**
5052
+ * Ensure vector index is built (for lazy mode).
5053
+ *
5054
+ * Drains the pending queue into a local batch before awaiting upserts so concurrent `index()` calls
5055
+ * append to a fresh queue and are not wiped by a blanket clear. Loops until the queue is empty so
5056
+ * documents added mid-flush are indexed before search runs. Re-queues the batch on flush failure.
4946
5057
  */
4947
5058
  async #ensureVectorIndex() {
4948
- if (!this.#lazyVectorIndex || this.#vectorIndexBuilt || this.#pendingVectorDocs.length === 0) {
5059
+ if (!this.#lazyVectorIndex) {
4949
5060
  return;
4950
5061
  }
4951
- for (const doc of this.#pendingVectorDocs) {
4952
- await this.#indexVector(doc);
5062
+ if (this.#pendingVectorDocs.length === 0) {
5063
+ this.#vectorIndexBuilt = true;
5064
+ return;
5065
+ }
5066
+ while (this.#pendingVectorDocs.length > 0) {
5067
+ const batch = this.#pendingVectorDocs;
5068
+ this.#pendingVectorDocs = [];
5069
+ const uniqueDocs = this.#dedupePendingVectorDocsLastWins(batch);
5070
+ try {
5071
+ await this.#flushVectorBatch(uniqueDocs);
5072
+ } catch (error) {
5073
+ this.#pendingVectorDocs = [...uniqueDocs, ...this.#pendingVectorDocs];
5074
+ throw error;
5075
+ }
4953
5076
  }
4954
- this.#pendingVectorDocs = [];
4955
5077
  this.#vectorIndexBuilt = true;
4956
5078
  }
4957
5079
  /**
@@ -4985,8 +5107,8 @@ var SearchEngine = class {
4985
5107
  throw new Error("Vector search requires vector configuration.");
4986
5108
  }
4987
5109
  await this.#ensureVectorIndex();
4988
- const { vectorStore, embedder, indexName } = this.#vectorConfig;
4989
- const queryEmbedding = await embedder(query);
5110
+ const { vectorStore, indexName } = this.#vectorConfig;
5111
+ const queryEmbedding = await this.#embedOne(query);
4990
5112
  const vectorResults = await vectorStore.query({
4991
5113
  indexName,
4992
5114
  queryVector: queryEmbedding,
@@ -6787,6 +6909,7 @@ Available files: ${allFiles.join(", ")}` : "";
6787
6909
  }
6788
6910
 
6789
6911
  // src/workspace/workspace.ts
6912
+ var FS_READ_CONCURRENCY = 8;
6790
6913
  var Workspace = class {
6791
6914
  id;
6792
6915
  name;
@@ -7120,20 +7243,15 @@ var Workspace = class {
7120
7243
  const alreadyCovered = directoryRoots.some((root) => entry.path === root || entry.path.startsWith(`${root}/`));
7121
7244
  if (!alreadyCovered) directoryRoots.push(entry.path);
7122
7245
  }
7123
- for (const filePath of filesToIndex) {
7124
- if (indexedPaths.has(filePath)) continue;
7125
- await this.indexFileForSearch(filePath);
7126
- indexedPaths.add(filePath);
7127
- }
7246
+ const indexed = await this.indexFilesForSearch(
7247
+ Array.from(filesToIndex).filter((filePath) => !indexedPaths.has(filePath))
7248
+ );
7249
+ for (const filePath of indexed) indexedPaths.add(filePath);
7128
7250
  for (const dir of directoryRoots) {
7129
7251
  try {
7130
- const files = await this.getAllFiles(dir);
7131
- for (const filePath of files) {
7132
- if (!indexedPaths.has(filePath)) {
7133
- await this.indexFileForSearch(filePath);
7134
- indexedPaths.add(filePath);
7135
- }
7136
- }
7252
+ const files = (await this.getAllFiles(dir)).filter((filePath) => !indexedPaths.has(filePath));
7253
+ const indexed2 = await this.indexFilesForSearch(files);
7254
+ for (const filePath of indexed2) indexedPaths.add(filePath);
7137
7255
  } catch {
7138
7256
  }
7139
7257
  }
@@ -7141,10 +7259,69 @@ var Workspace = class {
7141
7259
  }
7142
7260
  }
7143
7261
  }
7262
+ /**
7263
+ * Load file contents for search indexing in parallel (bounded by {@link FS_READ_CONCURRENCY}).
7264
+ * Paths that cannot be read as UTF-8 text are omitted (same behavior as {@link indexFileForSearch}).
7265
+ */
7266
+ async batchReadFiles(files) {
7267
+ if (!this._fs || files.length === 0) {
7268
+ return [];
7269
+ }
7270
+ const fs6 = this._fs;
7271
+ return pMap__default.default(
7272
+ files,
7273
+ async (filePath) => {
7274
+ try {
7275
+ const content = await fs6.readFile(filePath, { encoding: "utf-8" });
7276
+ const chunks = splitIntoChunks(content);
7277
+ const docs = chunks.length === 1 ? [{ id: filePath, content }] : chunks.map((chunk, i) => ({
7278
+ id: `${filePath}#chunk-${i}`,
7279
+ content: chunk.content,
7280
+ startLineOffset: chunk.startLine,
7281
+ metadata: { sourceFile: filePath }
7282
+ }));
7283
+ return { filePath, docs };
7284
+ } catch {
7285
+ return pMap.pMapSkip;
7286
+ }
7287
+ },
7288
+ { stopOnError: false, concurrency: FS_READ_CONCURRENCY }
7289
+ );
7290
+ }
7291
+ /**
7292
+ * Batch-read paths and {@link SearchEngine.indexMany}
7293
+ *
7294
+ * @returns paths that were indexed successfully.
7295
+ * @remarks Falls back to one-at-a-time indexing on failure of {@link SearchEngine.indexMany}
7296
+ */
7297
+ async indexFilesForSearch(paths) {
7298
+ const engine = this._searchEngine;
7299
+ if (!engine) return [];
7300
+ try {
7301
+ const entries = await this.batchReadFiles(paths);
7302
+ await pMap__default.default(entries, ({ filePath }) => engine.removeSource(filePath), {
7303
+ concurrency: FS_READ_CONCURRENCY
7304
+ });
7305
+ const docs = entries.flatMap(({ docs: docs2 }) => docs2);
7306
+ await engine.indexMany(docs);
7307
+ return entries.map(({ filePath }) => filePath);
7308
+ } catch {
7309
+ const indexed = [];
7310
+ for (const filePath of paths) {
7311
+ const id = await this.indexFileForSearch(filePath);
7312
+ if (id !== void 0) {
7313
+ indexed.push(id);
7314
+ }
7315
+ }
7316
+ return indexed;
7317
+ }
7318
+ }
7144
7319
  /**
7145
7320
  * Index a single file for search. Skips files that can't be read as text.
7146
7321
  * Large files are automatically split into chunks to stay within embedding
7147
7322
  * model token limits.
7323
+ *
7324
+ * @returns `filePath` when indexed, or `undefined` if read/index failed.
7148
7325
  */
7149
7326
  async indexFileForSearch(filePath) {
7150
7327
  let content;
@@ -7158,11 +7335,13 @@ var Workspace = class {
7158
7335
  if (chunks.length === 1) {
7159
7336
  try {
7160
7337
  await this._searchEngine.index({ id: filePath, content });
7338
+ return filePath;
7161
7339
  } catch (error) {
7162
7340
  this._logger?.warn(`Failed to index file "${filePath}" for search`, { error });
7341
+ return;
7163
7342
  }
7164
- return;
7165
7343
  }
7344
+ let anyIndexed = false;
7166
7345
  for (let i = 0; i < chunks.length; i++) {
7167
7346
  const chunk = chunks[i];
7168
7347
  try {
@@ -7172,10 +7351,12 @@ var Workspace = class {
7172
7351
  startLineOffset: chunk.startLine,
7173
7352
  metadata: { sourceFile: filePath }
7174
7353
  });
7354
+ anyIndexed = true;
7175
7355
  } catch (error) {
7176
7356
  this._logger?.warn(`Failed to index chunk ${i} of file "${filePath}" for search`, { error });
7177
7357
  }
7178
7358
  }
7359
+ return anyIndexed ? filePath : void 0;
7179
7360
  }
7180
7361
  async getAllFiles(dir, depth = 0, maxDepth = 10, filesystem = this._fs) {
7181
7362
  if (!filesystem || depth >= maxDepth) return [];
@@ -7527,7 +7708,7 @@ function isAstGrepAvailable() {
7527
7708
  return astGrepModule !== null;
7528
7709
  }
7529
7710
  try {
7530
- const req = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-OQ6ORZLY.cjs', document.baseURI).href)));
7711
+ const req = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('chunk-5ANWVE3P.cjs', document.baseURI).href)));
7531
7712
  req.resolve("@ast-grep/napi");
7532
7713
  return true;
7533
7714
  } catch {
@@ -9721,5 +9902,5 @@ exports.requireWorkspace = requireWorkspace;
9721
9902
  exports.resolveToolConfig = resolveToolConfig;
9722
9903
  exports.searchTool = searchTool;
9723
9904
  exports.writeFileTool = writeFileTool;
9724
- //# sourceMappingURL=chunk-OQ6ORZLY.cjs.map
9725
- //# sourceMappingURL=chunk-OQ6ORZLY.cjs.map
9905
+ //# sourceMappingURL=chunk-5ANWVE3P.cjs.map
9906
+ //# sourceMappingURL=chunk-5ANWVE3P.cjs.map