@sunub/obsidian-mcp-server 0.3.15 → 0.3.18

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 (224) hide show
  1. package/{build → dist}/config.d.ts +1 -1
  2. package/dist/config.d.ts.map +1 -0
  3. package/dist/config.js +59 -0
  4. package/dist/config.js.map +1 -0
  5. package/dist/db.js +58 -0
  6. package/dist/db.js.map +1 -0
  7. package/dist/index.js +52 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/server.js +42 -0
  10. package/dist/server.js.map +1 -0
  11. package/dist/setup.js +114 -0
  12. package/dist/setup.js.map +1 -0
  13. package/{build → dist}/tools/create_document_with_properties/index.d.ts +1 -1
  14. package/dist/tools/create_document_with_properties/index.js +115 -0
  15. package/dist/tools/create_document_with_properties/index.js.map +1 -0
  16. package/{build → dist}/tools/create_document_with_properties/params.d.ts +2 -2
  17. package/dist/tools/create_document_with_properties/params.js +33 -0
  18. package/dist/tools/create_document_with_properties/params.js.map +1 -0
  19. package/{build → dist}/tools/generate_property/index.d.ts +1 -1
  20. package/dist/tools/generate_property/index.js +86 -0
  21. package/dist/tools/generate_property/index.js.map +1 -0
  22. package/dist/tools/generate_property/params.js +55 -0
  23. package/dist/tools/generate_property/params.js.map +1 -0
  24. package/dist/tools/index.js +13 -0
  25. package/dist/tools/index.js.map +1 -0
  26. package/{build → dist}/tools/organize_attachments/index.d.ts +1 -1
  27. package/dist/tools/organize_attachments/index.js +86 -0
  28. package/dist/tools/organize_attachments/index.js.map +1 -0
  29. package/dist/tools/organize_attachments/params.js +61 -0
  30. package/dist/tools/organize_attachments/params.js.map +1 -0
  31. package/{build → dist}/tools/organize_attachments/utils.d.ts +1 -1
  32. package/dist/tools/organize_attachments/utils.js +66 -0
  33. package/dist/tools/organize_attachments/utils.js.map +1 -0
  34. package/{build → dist}/tools/vault/index.d.ts +1 -1
  35. package/dist/tools/vault/index.js +104 -0
  36. package/dist/tools/vault/index.js.map +1 -0
  37. package/dist/tools/vault/metrics.js +130 -0
  38. package/dist/tools/vault/metrics.js.map +1 -0
  39. package/{build → dist}/tools/vault/params.d.ts +2 -2
  40. package/dist/tools/vault/params.js +148 -0
  41. package/dist/tools/vault/params.js.map +1 -0
  42. package/dist/tools/vault/types/collect_context.js +103 -0
  43. package/dist/tools/vault/types/collect_context.js.map +1 -0
  44. package/dist/tools/vault/types/list_all.js +53 -0
  45. package/dist/tools/vault/types/list_all.js.map +1 -0
  46. package/dist/tools/vault/types/read_specific.js +41 -0
  47. package/dist/tools/vault/types/read_specific.js.map +1 -0
  48. package/dist/tools/vault/types/search.js +59 -0
  49. package/dist/tools/vault/types/search.js.map +1 -0
  50. package/dist/tools/vault/utils/actions/collect_context.d.ts +5 -0
  51. package/dist/tools/vault/utils/actions/collect_context.js +765 -0
  52. package/dist/tools/vault/utils/actions/collect_context.js.map +1 -0
  53. package/dist/tools/vault/utils/actions/index_rag.js +27 -0
  54. package/dist/tools/vault/utils/actions/index_rag.js.map +1 -0
  55. package/dist/tools/vault/utils/actions/list_all.d.ts +5 -0
  56. package/dist/tools/vault/utils/actions/list_all.js +43 -0
  57. package/dist/tools/vault/utils/actions/list_all.js.map +1 -0
  58. package/dist/tools/vault/utils/actions/load_memory.d.ts +5 -0
  59. package/dist/tools/vault/utils/actions/load_memory.js +186 -0
  60. package/dist/tools/vault/utils/actions/load_memory.js.map +1 -0
  61. package/{build → dist}/tools/vault/utils/actions/read.d.ts +2 -2
  62. package/dist/tools/vault/utils/actions/read.js +68 -0
  63. package/dist/tools/vault/utils/actions/read.js.map +1 -0
  64. package/{build → dist}/tools/vault/utils/actions/search.d.ts +2 -2
  65. package/dist/tools/vault/utils/actions/search.js +104 -0
  66. package/dist/tools/vault/utils/actions/search.js.map +1 -0
  67. package/{build → dist}/tools/vault/utils/actions/stats.d.ts +1 -1
  68. package/dist/tools/vault/utils/actions/stats.js +9 -0
  69. package/dist/tools/vault/utils/actions/stats.js.map +1 -0
  70. package/dist/tools/vault/utils/constants.js +3 -0
  71. package/dist/tools/vault/utils/constants.js.map +1 -0
  72. package/{build → dist}/tools/vault/utils/document.d.ts +3 -3
  73. package/dist/tools/vault/utils/document.js +50 -0
  74. package/dist/tools/vault/utils/document.js.map +1 -0
  75. package/{build → dist}/tools/vault/utils/shared.d.ts +1 -1
  76. package/dist/tools/vault/utils/shared.js +75 -0
  77. package/dist/tools/vault/utils/shared.js.map +1 -0
  78. package/dist/tools/vault/utils.d.ts +8 -0
  79. package/dist/tools/vault/utils.js +8 -0
  80. package/dist/tools/vault/utils.js.map +1 -0
  81. package/{build → dist}/tools/write_property/index.d.ts +1 -1
  82. package/dist/tools/write_property/index.js +120 -0
  83. package/dist/tools/write_property/index.js.map +1 -0
  84. package/dist/tools/write_property/params.js +52 -0
  85. package/dist/tools/write_property/params.js.map +1 -0
  86. package/{build → dist}/utils/DirectoryWalker.d.ts +1 -1
  87. package/dist/utils/DirectoryWalker.js +52 -0
  88. package/dist/utils/DirectoryWalker.js.map +1 -0
  89. package/dist/utils/Embedder.js +46 -0
  90. package/dist/utils/Embedder.js.map +1 -0
  91. package/{build → dist}/utils/Indexer.d.ts +2 -2
  92. package/dist/utils/Indexer.js +169 -0
  93. package/dist/utils/Indexer.js.map +1 -0
  94. package/dist/utils/LLMClient.js +112 -0
  95. package/dist/utils/LLMClient.js.map +1 -0
  96. package/dist/utils/LocalReranker.js +71 -0
  97. package/dist/utils/LocalReranker.js.map +1 -0
  98. package/{build → dist}/utils/RAGIndexer.d.ts +1 -1
  99. package/dist/utils/RAGIndexer.js +238 -0
  100. package/dist/utils/RAGIndexer.js.map +1 -0
  101. package/dist/utils/RerankerClient.js +89 -0
  102. package/dist/utils/RerankerClient.js.map +1 -0
  103. package/{build → dist}/utils/VaultManger/VaultManager.d.ts +3 -3
  104. package/dist/utils/VaultManger/VaultManager.js +459 -0
  105. package/dist/utils/VaultManger/VaultManager.js.map +1 -0
  106. package/dist/utils/VaultManger/VaultPathError.js +13 -0
  107. package/dist/utils/VaultManger/VaultPathError.js.map +1 -0
  108. package/dist/utils/VaultManger/index.d.ts +4 -0
  109. package/dist/utils/VaultManger/index.js +3 -0
  110. package/dist/utils/VaultManger/index.js.map +1 -0
  111. package/{build → dist}/utils/VaultManger/types.d.ts +1 -1
  112. package/dist/utils/VaultManger/types.js +2 -0
  113. package/dist/utils/VaultManger/types.js.map +1 -0
  114. package/dist/utils/VaultWatcher.js +60 -0
  115. package/dist/utils/VaultWatcher.js.map +1 -0
  116. package/dist/utils/VectorDB.js +268 -0
  117. package/dist/utils/VectorDB.js.map +1 -0
  118. package/dist/utils/constants.js +14 -0
  119. package/dist/utils/constants.js.map +1 -0
  120. package/dist/utils/createToolError.js +16 -0
  121. package/dist/utils/createToolError.js.map +1 -0
  122. package/dist/utils/getVaultManager.d.ts +3 -0
  123. package/dist/utils/getVaultManager.js +15 -0
  124. package/dist/utils/getVaultManager.js.map +1 -0
  125. package/dist/utils/processor/LinkExtractor.js +31 -0
  126. package/dist/utils/processor/LinkExtractor.js.map +1 -0
  127. package/dist/utils/processor/MatterParser.d.ts +3 -0
  128. package/dist/utils/processor/MatterParser.js +28 -0
  129. package/dist/utils/processor/MatterParser.js.map +1 -0
  130. package/dist/utils/processor/types.js +22 -0
  131. package/dist/utils/processor/types.js.map +1 -0
  132. package/dist/utils/semaphore.js +26 -0
  133. package/dist/utils/semaphore.js.map +1 -0
  134. package/package.json +19 -12
  135. package/build/config.d.ts.map +0 -1
  136. package/build/index.js +0 -127331
  137. package/build/index.js.map +0 -828
  138. package/build/lancedb.linux-x64-gnu-1e1af8yx.node +0 -0
  139. package/build/tools/vault/utils/actions/collect_context.d.ts +0 -5
  140. package/build/tools/vault/utils/actions/list_all.d.ts +0 -5
  141. package/build/tools/vault/utils/actions/load_memory.d.ts +0 -5
  142. package/build/tools/vault/utils.d.ts +0 -8
  143. package/build/utils/VaultManger/index.d.ts +0 -4
  144. package/build/utils/getVaultManager.d.ts +0 -3
  145. package/build/utils/processor/MatterParser.d.ts +0 -3
  146. /package/{build → dist}/db.d.ts +0 -0
  147. /package/{build → dist}/db.d.ts.map +0 -0
  148. /package/{build → dist}/index.d.ts +0 -0
  149. /package/{build → dist}/index.d.ts.map +0 -0
  150. /package/{build → dist}/server.d.ts +0 -0
  151. /package/{build → dist}/server.d.ts.map +0 -0
  152. /package/{build → dist}/setup.d.ts +0 -0
  153. /package/{build → dist}/setup.d.ts.map +0 -0
  154. /package/{build → dist}/tools/create_document_with_properties/index.d.ts.map +0 -0
  155. /package/{build → dist}/tools/create_document_with_properties/params.d.ts.map +0 -0
  156. /package/{build → dist}/tools/generate_property/index.d.ts.map +0 -0
  157. /package/{build → dist}/tools/generate_property/params.d.ts +0 -0
  158. /package/{build → dist}/tools/generate_property/params.d.ts.map +0 -0
  159. /package/{build → dist}/tools/index.d.ts +0 -0
  160. /package/{build → dist}/tools/index.d.ts.map +0 -0
  161. /package/{build → dist}/tools/organize_attachments/index.d.ts.map +0 -0
  162. /package/{build → dist}/tools/organize_attachments/params.d.ts +0 -0
  163. /package/{build → dist}/tools/organize_attachments/params.d.ts.map +0 -0
  164. /package/{build → dist}/tools/organize_attachments/utils.d.ts.map +0 -0
  165. /package/{build → dist}/tools/vault/index.d.ts.map +0 -0
  166. /package/{build → dist}/tools/vault/metrics.d.ts +0 -0
  167. /package/{build → dist}/tools/vault/metrics.d.ts.map +0 -0
  168. /package/{build → dist}/tools/vault/params.d.ts.map +0 -0
  169. /package/{build → dist}/tools/vault/types/collect_context.d.ts +0 -0
  170. /package/{build → dist}/tools/vault/types/collect_context.d.ts.map +0 -0
  171. /package/{build → dist}/tools/vault/types/list_all.d.ts +0 -0
  172. /package/{build → dist}/tools/vault/types/list_all.d.ts.map +0 -0
  173. /package/{build → dist}/tools/vault/types/read_specific.d.ts +0 -0
  174. /package/{build → dist}/tools/vault/types/read_specific.d.ts.map +0 -0
  175. /package/{build → dist}/tools/vault/types/search.d.ts +0 -0
  176. /package/{build → dist}/tools/vault/types/search.d.ts.map +0 -0
  177. /package/{build → dist}/tools/vault/utils/actions/collect_context.d.ts.map +0 -0
  178. /package/{build → dist}/tools/vault/utils/actions/index_rag.d.ts +0 -0
  179. /package/{build → dist}/tools/vault/utils/actions/index_rag.d.ts.map +0 -0
  180. /package/{build → dist}/tools/vault/utils/actions/list_all.d.ts.map +0 -0
  181. /package/{build → dist}/tools/vault/utils/actions/load_memory.d.ts.map +0 -0
  182. /package/{build → dist}/tools/vault/utils/actions/read.d.ts.map +0 -0
  183. /package/{build → dist}/tools/vault/utils/actions/search.d.ts.map +0 -0
  184. /package/{build → dist}/tools/vault/utils/actions/stats.d.ts.map +0 -0
  185. /package/{build → dist}/tools/vault/utils/constants.d.ts +0 -0
  186. /package/{build → dist}/tools/vault/utils/constants.d.ts.map +0 -0
  187. /package/{build → dist}/tools/vault/utils/document.d.ts.map +0 -0
  188. /package/{build → dist}/tools/vault/utils/shared.d.ts.map +0 -0
  189. /package/{build → dist}/tools/vault/utils.d.ts.map +0 -0
  190. /package/{build → dist}/tools/write_property/index.d.ts.map +0 -0
  191. /package/{build → dist}/tools/write_property/params.d.ts +0 -0
  192. /package/{build → dist}/tools/write_property/params.d.ts.map +0 -0
  193. /package/{build → dist}/utils/DirectoryWalker.d.ts.map +0 -0
  194. /package/{build → dist}/utils/Embedder.d.ts +0 -0
  195. /package/{build → dist}/utils/Embedder.d.ts.map +0 -0
  196. /package/{build → dist}/utils/Indexer.d.ts.map +0 -0
  197. /package/{build → dist}/utils/LLMClient.d.ts +0 -0
  198. /package/{build → dist}/utils/LLMClient.d.ts.map +0 -0
  199. /package/{build → dist}/utils/LocalReranker.d.ts +0 -0
  200. /package/{build → dist}/utils/LocalReranker.d.ts.map +0 -0
  201. /package/{build → dist}/utils/RAGIndexer.d.ts.map +0 -0
  202. /package/{build → dist}/utils/RerankerClient.d.ts +0 -0
  203. /package/{build → dist}/utils/RerankerClient.d.ts.map +0 -0
  204. /package/{build → dist}/utils/VaultManger/VaultManager.d.ts.map +0 -0
  205. /package/{build → dist}/utils/VaultManger/VaultPathError.d.ts +0 -0
  206. /package/{build → dist}/utils/VaultManger/VaultPathError.d.ts.map +0 -0
  207. /package/{build → dist}/utils/VaultManger/index.d.ts.map +0 -0
  208. /package/{build → dist}/utils/VaultManger/types.d.ts.map +0 -0
  209. /package/{build → dist}/utils/VaultWatcher.d.ts +0 -0
  210. /package/{build → dist}/utils/VaultWatcher.d.ts.map +0 -0
  211. /package/{build → dist}/utils/VectorDB.d.ts +0 -0
  212. /package/{build → dist}/utils/VectorDB.d.ts.map +0 -0
  213. /package/{build → dist}/utils/constants.d.ts +0 -0
  214. /package/{build → dist}/utils/constants.d.ts.map +0 -0
  215. /package/{build → dist}/utils/createToolError.d.ts +0 -0
  216. /package/{build → dist}/utils/createToolError.d.ts.map +0 -0
  217. /package/{build → dist}/utils/getVaultManager.d.ts.map +0 -0
  218. /package/{build → dist}/utils/processor/LinkExtractor.d.ts +0 -0
  219. /package/{build → dist}/utils/processor/LinkExtractor.d.ts.map +0 -0
  220. /package/{build → dist}/utils/processor/MatterParser.d.ts.map +0 -0
  221. /package/{build → dist}/utils/processor/types.d.ts +0 -0
  222. /package/{build → dist}/utils/processor/types.d.ts.map +0 -0
  223. /package/{build → dist}/utils/semaphore.d.ts +0 -0
  224. /package/{build → dist}/utils/semaphore.d.ts.map +0 -0
@@ -0,0 +1,765 @@
1
+ import { createHash } from "node:crypto";
2
+ import { debugLogger } from "@sunub/obsidian-mcp-core";
3
+ import { collectContextMemoryPacketSchema, collectContextPayloadSchema, collectContextResponseDataSchema, collectContextTokenV1Schema, } from "../../../../tools/vault/types/collect_context.js";
4
+ import { CONTEXT_MEMORY_SNAPSHOT_NOTE_PATH, CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION, } from "../../../../tools/vault/utils/constants.js";
5
+ import { ACTION_DEFAULT_MAX_OUTPUT_CHARS, finalizePayloadWithCompression, jsonCharLength, normalizeWhitespace, resolveCompressionMode, stripFrontmatterBlock, trimWithEllipsis, } from "../../../../tools/vault/utils/shared.js";
6
+ import { createToolError } from "../../../../utils/createToolError.js";
7
+ import { rerankerClient } from "../../../../utils/RerankerClient.js";
8
+ const COLLECT_CONTEXT_CACHE_MAX_ENTRIES = 200;
9
+ const COLLECT_CONTEXT_MIN_EXCERPT_CHARS = 220;
10
+ const COLLECT_CONTEXT_MIN_SUMMARY_CHARS = 120;
11
+ const COLLECT_CONTEXT_MIN_EVIDENCE_CHARS = 80;
12
+ const collectContextCache = new Map();
13
+ function pickEvidenceSnippets(content, maxSnippets) {
14
+ const lines = stripFrontmatterBlock(content)
15
+ .split("\n")
16
+ .map((line) => line.trim())
17
+ .filter((line) => line.length >= 24);
18
+ const prioritized = [
19
+ ...lines.filter((line) => /^([-*]|\d+[.)])\s+/.test(line)),
20
+ ...lines.filter((line) => /^#{1,6}\s+/.test(line)),
21
+ ...lines.filter((line) => !/^([-*]|\d+[.)])\s+/.test(line) && !/^#{1,6}\s+/.test(line)),
22
+ ];
23
+ const unique = [...new Set(prioritized)].map((line) => normalizeWhitespace(line.replace(/^#{1,6}\s+/, "").replace(/^[-*]\s+/, "")));
24
+ return unique
25
+ .slice(0, maxSnippets)
26
+ .map((line) => trimWithEllipsis(line, 220));
27
+ }
28
+ function inferCollectContextRelevance(doc, topic) {
29
+ if (!topic) {
30
+ return "medium";
31
+ }
32
+ const normalizedTopic = topic.toLowerCase();
33
+ if (doc.title.toLowerCase().includes(normalizedTopic) ||
34
+ doc.tags.some((tag) => tag.toLowerCase().includes(normalizedTopic))) {
35
+ return "high";
36
+ }
37
+ if (doc.excerpt.toLowerCase().includes(normalizedTopic)) {
38
+ return "medium";
39
+ }
40
+ return "low";
41
+ }
42
+ function buildCollectContextDocument(doc, maxCharsPerDoc, topic) {
43
+ const filename = doc.filePath.split("/").pop() || doc.filePath;
44
+ const title = doc.frontmatter.title || filename.replace(/\.mdx?$/i, "");
45
+ const tags = Array.isArray(doc.frontmatter.tags)
46
+ ? doc.frontmatter.tags.filter((tag) => typeof tag === "string")
47
+ : [];
48
+ const sourceContent = stripFrontmatterBlock(doc.content);
49
+ const excerpt = trimWithEllipsis(normalizeWhitespace(sourceContent), maxCharsPerDoc);
50
+ const evidenceSnippets = pickEvidenceSnippets(sourceContent, 2);
51
+ const summary = evidenceSnippets.join(" ").trim() ||
52
+ trimWithEllipsis(excerpt, 220) ||
53
+ title;
54
+ const sourceContentLength = doc.stats?.contentLength ?? sourceContent.length;
55
+ const stats = {
56
+ contentLength: sourceContentLength,
57
+ wordCount: doc.stats?.wordCount ?? 0,
58
+ hasContent: doc.stats?.hasContent ?? sourceContent.trim().length > 0,
59
+ };
60
+ const docHash = doc.contentHash ??
61
+ createHash("sha256").update(sourceContent, "utf8").digest("hex");
62
+ const relevance = inferCollectContextRelevance({
63
+ title,
64
+ tags,
65
+ excerpt,
66
+ }, topic);
67
+ return {
68
+ filename,
69
+ fullPath: doc.filePath,
70
+ title,
71
+ tags,
72
+ doc_hash: docHash,
73
+ summary,
74
+ excerpt,
75
+ evidence_snippets: evidenceSnippets,
76
+ relevance,
77
+ stats,
78
+ backlinks_count: doc.backlinks?.length ?? 0,
79
+ truncated: sourceContentLength > excerpt.length,
80
+ };
81
+ }
82
+ function buildCollectContextMemoryPacket(topic, documents) {
83
+ const keyFacts = documents
84
+ .flatMap((doc) => doc.evidence_snippets.map((snippet) => `${doc.title}: ${snippet}`))
85
+ .slice(0, 10);
86
+ const experienceBullets = documents
87
+ .map((doc) => `${doc.title}: ${trimWithEllipsis(doc.summary, 180)}`)
88
+ .slice(0, 8);
89
+ const sourceRefs = documents.slice(0, 10).map((doc) => ({
90
+ filePath: doc.fullPath,
91
+ title: doc.title,
92
+ relevance: doc.relevance,
93
+ evidenceSnippets: doc.evidence_snippets.slice(0, 2),
94
+ }));
95
+ const topicSummary = documents.length === 0
96
+ ? topic
97
+ ? `No evidence was collected for topic "${topic}".`
98
+ : "No documents were collected."
99
+ : trimWithEllipsis(documents
100
+ .slice(0, 3)
101
+ .map((doc) => `${doc.title}: ${doc.summary}`)
102
+ .join(" "), 550);
103
+ const openQuestions = [];
104
+ if (documents.length === 0) {
105
+ openQuestions.push(topic
106
+ ? `Should we widen the query beyond "${topic}"?`
107
+ : "Should we provide a narrower topic for better precision?");
108
+ }
109
+ if (documents.some((doc) => doc.truncated)) {
110
+ openQuestions.push("Some excerpts were truncated. Do we need full reads for high-priority notes?");
111
+ }
112
+ if (documents.length > 0 && documents.length < 3) {
113
+ openQuestions.push("Do we need additional sources before using this as final memory context?");
114
+ }
115
+ const truncationRatio = documents.length === 0
116
+ ? 1
117
+ : documents.filter((doc) => doc.truncated).length / documents.length;
118
+ const baseConfidence = documents.length === 0
119
+ ? 0.25
120
+ : Math.min(0.9, 0.45 + documents.length * 0.08);
121
+ const confidence = Number(Math.max(0.1, Math.min(0.95, baseConfidence - truncationRatio * 0.2)).toFixed(2));
122
+ return collectContextMemoryPacketSchema.parse({
123
+ topicSummary,
124
+ keyFacts,
125
+ experienceBullets,
126
+ sourceRefs,
127
+ openQuestions,
128
+ confidence,
129
+ });
130
+ }
131
+ function buildCollectContextPayload(params) {
132
+ return collectContextPayloadSchema.parse({
133
+ action: "collect_context",
134
+ scope: params.scope,
135
+ topic: params.topic,
136
+ matched_total: params.matchedTotal,
137
+ total_in_vault: params.totalInVault,
138
+ documents: params.documents,
139
+ memory_packet: buildCollectContextMemoryPacket(params.topic, params.documents),
140
+ memory_mode: params.memoryMode,
141
+ memory_write: params.memoryWriteRequested
142
+ ? {
143
+ requested: true,
144
+ status: "failed",
145
+ reason: "Memory note write requested but no write result was provided.",
146
+ }
147
+ : { requested: false, status: "not_requested" },
148
+ cache: params.cache,
149
+ batch: {
150
+ start_cursor: params.startCursor,
151
+ processed_docs: params.processedDocs,
152
+ consumed_candidates: params.consumedCandidates,
153
+ max_docs: params.maxDocs,
154
+ max_chars_per_doc: params.maxCharsPerDoc,
155
+ has_more: params.hasMore,
156
+ continuation_token: params.continuationToken,
157
+ },
158
+ });
159
+ }
160
+ function reduceCollectContextBacklinksForBudget(documents) {
161
+ let changed = false;
162
+ for (const doc of documents) {
163
+ if (doc.backlinks_count <= 0) {
164
+ continue;
165
+ }
166
+ let nextCount = doc.backlinks_count;
167
+ if (doc.backlinks_count > 10) {
168
+ nextCount = 10;
169
+ }
170
+ else if (doc.backlinks_count > 5) {
171
+ nextCount = 5;
172
+ }
173
+ else if (doc.backlinks_count > 3) {
174
+ nextCount = 3;
175
+ }
176
+ else {
177
+ nextCount = 0;
178
+ }
179
+ if (nextCount !== doc.backlinks_count) {
180
+ doc.backlinks_count = nextCount;
181
+ changed = true;
182
+ }
183
+ }
184
+ return changed;
185
+ }
186
+ function reduceCollectContextPerDocCharsForBudget(documents) {
187
+ let changed = false;
188
+ for (const doc of documents) {
189
+ if (doc.excerpt.length > COLLECT_CONTEXT_MIN_EXCERPT_CHARS) {
190
+ const nextLength = Math.max(COLLECT_CONTEXT_MIN_EXCERPT_CHARS, Math.floor(doc.excerpt.length * 0.8));
191
+ const nextExcerpt = trimWithEllipsis(doc.excerpt, nextLength);
192
+ if (nextExcerpt !== doc.excerpt) {
193
+ doc.excerpt = nextExcerpt;
194
+ doc.truncated = true;
195
+ changed = true;
196
+ }
197
+ }
198
+ if (doc.summary.length > COLLECT_CONTEXT_MIN_SUMMARY_CHARS) {
199
+ const nextLength = Math.max(COLLECT_CONTEXT_MIN_SUMMARY_CHARS, Math.floor(doc.summary.length * 0.85));
200
+ const nextSummary = trimWithEllipsis(doc.summary, nextLength);
201
+ if (nextSummary !== doc.summary) {
202
+ doc.summary = nextSummary;
203
+ doc.truncated = true;
204
+ changed = true;
205
+ }
206
+ }
207
+ const nextEvidence = doc.evidence_snippets.map((snippet) => {
208
+ if (snippet.length <= COLLECT_CONTEXT_MIN_EVIDENCE_CHARS) {
209
+ return snippet;
210
+ }
211
+ const nextLength = Math.max(COLLECT_CONTEXT_MIN_EVIDENCE_CHARS, Math.floor(snippet.length * 0.8));
212
+ return trimWithEllipsis(snippet, nextLength);
213
+ });
214
+ if (JSON.stringify(nextEvidence) !== JSON.stringify(doc.evidence_snippets)) {
215
+ doc.evidence_snippets = nextEvidence;
216
+ doc.truncated = true;
217
+ changed = true;
218
+ }
219
+ }
220
+ return changed;
221
+ }
222
+ function buildCollectContextDocHash(documents) {
223
+ return createHash("sha256")
224
+ .update(JSON.stringify(documents.map((doc) => ({
225
+ fullPath: doc.fullPath,
226
+ doc_hash: doc.doc_hash,
227
+ }))), "utf8")
228
+ .digest("hex");
229
+ }
230
+ function buildCollectContextCacheKey(params) {
231
+ return JSON.stringify({
232
+ scope: params.scope,
233
+ topic: params.topic,
234
+ doc_hash: params.docHash,
235
+ schema_version: CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION,
236
+ mode: params.mode,
237
+ start_cursor: params.startCursor,
238
+ max_docs: params.maxDocs,
239
+ max_chars_per_doc: params.maxCharsPerDoc,
240
+ });
241
+ }
242
+ function getCollectContextCache(key) {
243
+ const entry = collectContextCache.get(key);
244
+ if (!entry) {
245
+ return null;
246
+ }
247
+ entry.createdAt = Date.now();
248
+ return structuredClone(entry.payload);
249
+ }
250
+ function setCollectContextCache(key, payload) {
251
+ const cachedPayload = structuredClone(payload);
252
+ collectContextCache.set(key, {
253
+ key,
254
+ payload: cachedPayload,
255
+ createdAt: Date.now(),
256
+ });
257
+ if (collectContextCache.size > COLLECT_CONTEXT_CACHE_MAX_ENTRIES) {
258
+ const oldest = [...collectContextCache.values()].sort((a, b) => a.createdAt - b.createdAt)[0];
259
+ if (oldest) {
260
+ collectContextCache.delete(oldest.key);
261
+ }
262
+ }
263
+ return structuredClone(cachedPayload);
264
+ }
265
+ function buildCollectContextSourceHash(payload) {
266
+ const normalized = {
267
+ scope: payload.scope,
268
+ topic: payload.topic,
269
+ matched_total: payload.matched_total,
270
+ documents: payload.documents.map((doc) => ({
271
+ fullPath: doc.fullPath,
272
+ doc_hash: doc.doc_hash,
273
+ title: doc.title,
274
+ tags: doc.tags,
275
+ summary: doc.summary,
276
+ relevance: doc.relevance,
277
+ contentLength: doc.stats.contentLength,
278
+ backlinks_count: doc.backlinks_count,
279
+ truncated: doc.truncated,
280
+ })),
281
+ memory_packet: payload.memory_packet,
282
+ };
283
+ return createHash("sha256")
284
+ .update(JSON.stringify(normalized), "utf8")
285
+ .digest("hex");
286
+ }
287
+ function buildContextMemorySnapshotMarkdown(params) {
288
+ const { payload, generatedAt, sourceHash, notePath } = params;
289
+ const canonical = {
290
+ schema_version: CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION,
291
+ generated_at: generatedAt,
292
+ source_hash: sourceHash,
293
+ note_path: notePath,
294
+ action: payload.action,
295
+ scope: payload.scope,
296
+ topic: payload.topic,
297
+ matched_total: payload.matched_total,
298
+ total_in_vault: payload.total_in_vault,
299
+ documents: payload.documents.map((doc) => ({
300
+ fullPath: doc.fullPath,
301
+ doc_hash: doc.doc_hash,
302
+ title: doc.title,
303
+ tags: doc.tags,
304
+ relevance: doc.relevance,
305
+ summary: doc.summary,
306
+ evidence_snippets: doc.evidence_snippets,
307
+ stats: doc.stats,
308
+ backlinks_count: doc.backlinks_count,
309
+ truncated: doc.truncated,
310
+ })),
311
+ memory_packet: payload.memory_packet,
312
+ };
313
+ const keyFacts = payload.memory_packet.keyFacts
314
+ .slice(0, 8)
315
+ .map((fact) => `- ${fact}`)
316
+ .join("\n");
317
+ const experienceBullets = payload.memory_packet.experienceBullets
318
+ .slice(0, 8)
319
+ .map((item) => `- ${item}`)
320
+ .join("\n");
321
+ const sourceRefs = payload.memory_packet.sourceRefs
322
+ .slice(0, 10)
323
+ .map((ref) => `- [${ref.relevance}] ${ref.title} (${ref.filePath})${ref.evidenceSnippets.length > 0 ? `\n - ${ref.evidenceSnippets.join("\n - ")}` : ""}`)
324
+ .join("\n");
325
+ const openQuestions = payload.memory_packet.openQuestions.length > 0
326
+ ? payload.memory_packet.openQuestions.map((q) => `- ${q}`).join("\n")
327
+ : "- None";
328
+ return [
329
+ "# Context Memory Snapshot v1",
330
+ "",
331
+ `- generated_at: ${generatedAt}`,
332
+ `- source_hash: ${sourceHash}`,
333
+ `- schema_version: ${CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION}`,
334
+ `- topic: ${payload.topic ?? "null"}`,
335
+ `- scope: ${payload.scope}`,
336
+ `- matched_total: ${payload.matched_total}`,
337
+ `- total_in_vault: ${payload.total_in_vault}`,
338
+ "",
339
+ "## Topic Summary",
340
+ payload.memory_packet.topicSummary || "(empty)",
341
+ "",
342
+ "## Key Facts",
343
+ keyFacts || "- None",
344
+ "",
345
+ "## Experience Bullets",
346
+ experienceBullets || "- None",
347
+ "",
348
+ "## Source Refs",
349
+ sourceRefs || "- None",
350
+ "",
351
+ "## Open Questions",
352
+ openQuestions,
353
+ "",
354
+ "## Confidence",
355
+ `${payload.memory_packet.confidence}`,
356
+ "",
357
+ "## Canonical JSON",
358
+ "```json",
359
+ JSON.stringify(canonical, null, 2),
360
+ "```",
361
+ "",
362
+ ].join("\n");
363
+ }
364
+ async function writeContextMemorySnapshotNote(vaultManager, payload) {
365
+ const generatedAt = new Date().toISOString();
366
+ const sourceHash = buildCollectContextSourceHash(payload);
367
+ const markdown = buildContextMemorySnapshotMarkdown({
368
+ payload,
369
+ generatedAt,
370
+ sourceHash,
371
+ notePath: CONTEXT_MEMORY_SNAPSHOT_NOTE_PATH,
372
+ });
373
+ try {
374
+ await vaultManager.writeRawDocument(CONTEXT_MEMORY_SNAPSHOT_NOTE_PATH, markdown);
375
+ return {
376
+ requested: true,
377
+ status: "written",
378
+ note_path: CONTEXT_MEMORY_SNAPSHOT_NOTE_PATH,
379
+ generated_at: generatedAt,
380
+ source_hash: sourceHash,
381
+ };
382
+ }
383
+ catch (error) {
384
+ return {
385
+ requested: true,
386
+ status: "failed",
387
+ note_path: CONTEXT_MEMORY_SNAPSHOT_NOTE_PATH,
388
+ generated_at: generatedAt,
389
+ source_hash: sourceHash,
390
+ reason: error instanceof Error ? error.message : String(error),
391
+ };
392
+ }
393
+ }
394
+ function trimCollectContextPayloadToOutputLimit(payload, maxOutputChars) {
395
+ const next = structuredClone(payload);
396
+ let clamped = false;
397
+ let droppedDocCount = 0;
398
+ if (jsonCharLength(next) > maxOutputChars &&
399
+ reduceCollectContextBacklinksForBudget(next.documents)) {
400
+ clamped = true;
401
+ }
402
+ while (jsonCharLength(next) > maxOutputChars) {
403
+ const reduced = reduceCollectContextPerDocCharsForBudget(next.documents);
404
+ if (!reduced) {
405
+ break;
406
+ }
407
+ clamped = true;
408
+ }
409
+ while (jsonCharLength(next) > maxOutputChars && next.documents.length > 1) {
410
+ next.documents.pop();
411
+ droppedDocCount++;
412
+ clamped = true;
413
+ }
414
+ if (droppedDocCount > 0) {
415
+ next.batch.processed_docs = next.documents.length;
416
+ next.batch.consumed_candidates = Math.max(0, next.batch.consumed_candidates - droppedDocCount);
417
+ const continuationCursor = next.batch.start_cursor + next.batch.consumed_candidates;
418
+ next.batch.has_more = true;
419
+ next.batch.continuation_token = encodeCollectContextContinuationToken({
420
+ v: 1,
421
+ cursor: continuationCursor,
422
+ scope: next.scope,
423
+ topic: next.topic,
424
+ maxDocs: next.batch.max_docs,
425
+ maxCharsPerDoc: next.batch.max_chars_per_doc,
426
+ memoryMode: next.memory_mode,
427
+ });
428
+ }
429
+ while (jsonCharLength(next) > maxOutputChars &&
430
+ next.memory_packet.keyFacts.length > 3) {
431
+ next.memory_packet.keyFacts.pop();
432
+ clamped = true;
433
+ }
434
+ while (jsonCharLength(next) > maxOutputChars &&
435
+ next.memory_packet.experienceBullets.length > 3) {
436
+ next.memory_packet.experienceBullets.pop();
437
+ clamped = true;
438
+ }
439
+ while (jsonCharLength(next) > maxOutputChars &&
440
+ next.memory_packet.sourceRefs.length > 2) {
441
+ next.memory_packet.sourceRefs.pop();
442
+ clamped = true;
443
+ }
444
+ while (jsonCharLength(next) > maxOutputChars &&
445
+ next.memory_packet.openQuestions.length > 1) {
446
+ next.memory_packet.openQuestions.pop();
447
+ clamped = true;
448
+ }
449
+ if (jsonCharLength(next) > maxOutputChars &&
450
+ next.memory_packet.topicSummary.length > 200) {
451
+ next.memory_packet.topicSummary = trimWithEllipsis(next.memory_packet.topicSummary, 200);
452
+ clamped = true;
453
+ }
454
+ return { payload: next, clamped };
455
+ }
456
+ function encodeCollectContextContinuationToken(token) {
457
+ return Buffer.from(JSON.stringify(token), "utf8").toString("base64url");
458
+ }
459
+ function decodeCollectContextContinuationToken(rawToken) {
460
+ try {
461
+ const parsed = JSON.parse(Buffer.from(rawToken, "base64url").toString("utf8"));
462
+ const validation = collectContextTokenV1Schema.safeParse(parsed);
463
+ if (!validation.success) {
464
+ return null;
465
+ }
466
+ return {
467
+ v: 1,
468
+ cursor: Math.floor(validation.data.cursor),
469
+ scope: validation.data.scope,
470
+ topic: validation.data.topic,
471
+ maxDocs: Math.floor(validation.data.maxDocs),
472
+ maxCharsPerDoc: Math.floor(validation.data.maxCharsPerDoc),
473
+ memoryMode: validation.data.memoryMode,
474
+ };
475
+ }
476
+ catch {
477
+ return null;
478
+ }
479
+ }
480
+ export async function collectContext(vaultManager, params) {
481
+ await vaultManager.initialize();
482
+ const mode = resolveCompressionMode(params);
483
+ const continuation = params.continuationToken
484
+ ? decodeCollectContextContinuationToken(params.continuationToken)
485
+ : null;
486
+ if (params.continuationToken && !continuation) {
487
+ return createToolError("Invalid continuationToken for collect_context action", "Use the continuation_token value returned by a previous collect_context call.");
488
+ }
489
+ const scope = continuation?.scope ?? params.scope ?? "topic";
490
+ const topic = continuation?.topic ?? (params.topic?.trim() ? params.topic.trim() : null);
491
+ const maxDocs = continuation?.maxDocs ?? params.maxDocs ?? 20;
492
+ const maxCharsPerDoc = continuation?.maxCharsPerDoc ?? params.maxCharsPerDoc ?? 1800;
493
+ const memoryMode = continuation?.memoryMode ?? params.memoryMode ?? "response_only";
494
+ const startCursor = continuation?.cursor ?? 0;
495
+ if (scope === "topic" && !topic) {
496
+ return createToolError("topic parameter is required for collect_context when scope='topic'", 'Provide a topic, e.g. { action: "collect_context", topic: "next.js", scope: "topic" }');
497
+ }
498
+ const [allDocuments, matchedCandidates] = await Promise.all([
499
+ vaultManager.getAllDocuments(),
500
+ scope === "all"
501
+ ? vaultManager.getAllDocuments()
502
+ : vaultManager.searchDocuments(topic ?? ""),
503
+ ]);
504
+ let orderedCandidates = [...matchedCandidates].sort((a, b) => a.filePath.localeCompare(b.filePath));
505
+ // Apply Reranking if topic is present and we have candidates
506
+ if (topic && orderedCandidates.length > 0) {
507
+ // Only rerank a reasonable number of top candidates to preserve performance
508
+ const rerankCandidatesLimit = 30;
509
+ const candidatesToRerank = orderedCandidates.slice(0, rerankCandidatesLimit);
510
+ try {
511
+ // Fetch contents for these candidates to perform reranking
512
+ const candidateContents = await Promise.all(candidatesToRerank.map(async (c) => {
513
+ const doc = await vaultManager.getDocumentInfo(c.filePath, {
514
+ maxContentPreview: 1000, // Sufficient for reranking
515
+ });
516
+ return doc?.content || "";
517
+ }));
518
+ const rerankedResults = await rerankerClient.rerank(topic, candidateContents, maxDocs);
519
+ const reranked = rerankedResults.map((result) => candidatesToRerank[result.index]);
520
+ const remaining = orderedCandidates.filter((c) => !reranked.some((r) => r.filePath === c.filePath));
521
+ orderedCandidates = [...reranked, ...remaining];
522
+ }
523
+ catch (error) {
524
+ debugLogger.error("[CollectContext] Reranking failed:", error);
525
+ }
526
+ }
527
+ const emptyDocHash = buildCollectContextDocHash([]);
528
+ const emptyCacheKey = buildCollectContextCacheKey({
529
+ scope,
530
+ topic,
531
+ docHash: emptyDocHash,
532
+ mode: memoryMode,
533
+ startCursor,
534
+ maxDocs,
535
+ maxCharsPerDoc,
536
+ });
537
+ if (orderedCandidates.length === 0 ||
538
+ startCursor >= orderedCandidates.length) {
539
+ const cachedEmptyPayload = getCollectContextCache(emptyCacheKey);
540
+ const emptyCacheHit = cachedEmptyPayload !== null;
541
+ let emptyPayload = cachedEmptyPayload ??
542
+ setCollectContextCache(emptyCacheKey, buildCollectContextPayload({
543
+ scope,
544
+ topic,
545
+ matchedTotal: orderedCandidates.length,
546
+ totalInVault: allDocuments.length,
547
+ documents: [],
548
+ memoryMode,
549
+ memoryWriteRequested: false,
550
+ cache: {
551
+ key: emptyCacheKey,
552
+ hit: false,
553
+ schema_version: CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION,
554
+ topic,
555
+ doc_hash: emptyDocHash,
556
+ mode: memoryMode,
557
+ },
558
+ startCursor,
559
+ processedDocs: 0,
560
+ consumedCandidates: 0,
561
+ maxDocs,
562
+ maxCharsPerDoc,
563
+ hasMore: false,
564
+ continuationToken: null,
565
+ }));
566
+ if (emptyCacheHit && emptyPayload.cache) {
567
+ emptyPayload = collectContextPayloadSchema.parse({
568
+ ...emptyPayload,
569
+ cache: {
570
+ ...emptyPayload.cache,
571
+ hit: true,
572
+ },
573
+ });
574
+ }
575
+ if (memoryMode !== "response_only") {
576
+ const memoryWrite = await writeContextMemorySnapshotNote(vaultManager, emptyPayload);
577
+ emptyPayload = collectContextPayloadSchema.parse({
578
+ ...emptyPayload,
579
+ memory_mode: memoryMode,
580
+ memory_write: memoryWrite,
581
+ });
582
+ }
583
+ const finalizedPayload = collectContextResponseDataSchema.parse(finalizePayloadWithCompression(emptyPayload, {
584
+ mode,
585
+ source_chars: 0,
586
+ max_output_chars: params.maxOutputChars ??
587
+ (mode === "none"
588
+ ? null
589
+ : ACTION_DEFAULT_MAX_OUTPUT_CHARS.collect_context[mode]),
590
+ truncated: false,
591
+ expand_hint: "If more context is needed, rerun collect_context with broader scope or topic.",
592
+ }));
593
+ return {
594
+ isError: false,
595
+ content: [
596
+ { type: "text", text: JSON.stringify(finalizedPayload, null, 2) },
597
+ ],
598
+ };
599
+ }
600
+ const maxOutputChars = params.maxOutputChars ??
601
+ (mode === "none"
602
+ ? null
603
+ : ACTION_DEFAULT_MAX_OUTPUT_CHARS.collect_context[mode]);
604
+ const documents = [];
605
+ let sourceChars = 0;
606
+ let consumedCandidates = 0;
607
+ let outputCapClamped = false;
608
+ let scannedIndex = startCursor;
609
+ while (scannedIndex < orderedCandidates.length &&
610
+ documents.length < maxDocs) {
611
+ const candidate = orderedCandidates[scannedIndex];
612
+ scannedIndex++;
613
+ consumedCandidates++;
614
+ const enrichedDoc = await vaultManager.getDocumentInfo(candidate.filePath, {
615
+ includeStats: true,
616
+ includeBacklinks: true,
617
+ includeContentHash: true,
618
+ maxContentPreview: maxCharsPerDoc,
619
+ });
620
+ if (!enrichedDoc) {
621
+ continue;
622
+ }
623
+ const nextDoc = buildCollectContextDocument(enrichedDoc, maxCharsPerDoc, topic);
624
+ documents.push(nextDoc);
625
+ sourceChars += nextDoc.stats.contentLength;
626
+ if (typeof maxOutputChars === "number") {
627
+ const buildProvisionalPayload = () => buildCollectContextPayload({
628
+ scope,
629
+ topic,
630
+ matchedTotal: orderedCandidates.length,
631
+ totalInVault: allDocuments.length,
632
+ documents,
633
+ memoryMode,
634
+ memoryWriteRequested: false,
635
+ startCursor,
636
+ processedDocs: documents.length,
637
+ consumedCandidates,
638
+ maxDocs,
639
+ maxCharsPerDoc,
640
+ hasMore: scannedIndex < orderedCandidates.length,
641
+ continuationToken: null,
642
+ });
643
+ let provisionalPayload = buildProvisionalPayload();
644
+ if (jsonCharLength(provisionalPayload) > maxOutputChars) {
645
+ outputCapClamped = true;
646
+ if (reduceCollectContextBacklinksForBudget(documents)) {
647
+ provisionalPayload = buildProvisionalPayload();
648
+ }
649
+ while (jsonCharLength(provisionalPayload) > maxOutputChars) {
650
+ const reduced = reduceCollectContextPerDocCharsForBudget(documents);
651
+ if (!reduced) {
652
+ break;
653
+ }
654
+ provisionalPayload = buildProvisionalPayload();
655
+ }
656
+ if (jsonCharLength(provisionalPayload) > maxOutputChars) {
657
+ if (documents.length > 1) {
658
+ const removedDoc = documents.pop();
659
+ if (removedDoc) {
660
+ sourceChars -= removedDoc.stats.contentLength;
661
+ }
662
+ consumedCandidates--;
663
+ scannedIndex--;
664
+ }
665
+ break;
666
+ }
667
+ }
668
+ }
669
+ }
670
+ const nextCursor = startCursor + consumedCandidates;
671
+ const hasMore = nextCursor < orderedCandidates.length;
672
+ const continuationToken = hasMore
673
+ ? encodeCollectContextContinuationToken({
674
+ v: 1,
675
+ cursor: nextCursor,
676
+ scope,
677
+ topic,
678
+ maxDocs,
679
+ maxCharsPerDoc,
680
+ memoryMode,
681
+ })
682
+ : null;
683
+ const docHash = buildCollectContextDocHash(documents);
684
+ const cacheKey = buildCollectContextCacheKey({
685
+ scope,
686
+ topic,
687
+ docHash: docHash,
688
+ mode: memoryMode,
689
+ startCursor,
690
+ maxDocs,
691
+ maxCharsPerDoc,
692
+ });
693
+ const cachedPayload = getCollectContextCache(cacheKey);
694
+ const cacheHit = cachedPayload !== null;
695
+ const basePayload = cachedPayload ??
696
+ setCollectContextCache(cacheKey, buildCollectContextPayload({
697
+ scope,
698
+ topic,
699
+ matchedTotal: orderedCandidates.length,
700
+ totalInVault: allDocuments.length,
701
+ documents,
702
+ memoryMode,
703
+ memoryWriteRequested: false,
704
+ cache: {
705
+ key: cacheKey,
706
+ hit: false,
707
+ schema_version: CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION,
708
+ topic,
709
+ doc_hash: docHash,
710
+ mode: memoryMode,
711
+ },
712
+ startCursor,
713
+ processedDocs: documents.length,
714
+ consumedCandidates,
715
+ maxDocs,
716
+ maxCharsPerDoc,
717
+ hasMore,
718
+ continuationToken,
719
+ }));
720
+ let payloadForCompression = cacheHit
721
+ ? collectContextPayloadSchema.parse({
722
+ ...basePayload,
723
+ cache: basePayload.cache
724
+ ? {
725
+ ...basePayload.cache,
726
+ hit: true,
727
+ }
728
+ : {
729
+ key: cacheKey,
730
+ hit: true,
731
+ schema_version: CONTEXT_MEMORY_SNAPSHOT_SCHEMA_VERSION,
732
+ topic,
733
+ doc_hash: docHash,
734
+ mode: memoryMode,
735
+ },
736
+ })
737
+ : basePayload;
738
+ if (memoryMode !== "response_only") {
739
+ const memoryWrite = await writeContextMemorySnapshotNote(vaultManager, basePayload);
740
+ payloadForCompression = collectContextPayloadSchema.parse({
741
+ ...basePayload,
742
+ memory_mode: memoryMode,
743
+ memory_write: memoryWrite,
744
+ });
745
+ }
746
+ if (typeof maxOutputChars === "number") {
747
+ const trimmed = trimCollectContextPayloadToOutputLimit(payloadForCompression, maxOutputChars);
748
+ payloadForCompression = trimmed.payload;
749
+ outputCapClamped = outputCapClamped || trimmed.clamped;
750
+ }
751
+ const finalizedPayload = collectContextResponseDataSchema.parse(finalizePayloadWithCompression(payloadForCompression, {
752
+ mode,
753
+ source_chars: sourceChars,
754
+ max_output_chars: maxOutputChars,
755
+ truncated: hasMore || documents.some((doc) => doc.truncated) || outputCapClamped,
756
+ expand_hint: "If has_more is true, call collect_context again with continuationToken.",
757
+ }));
758
+ return {
759
+ isError: false,
760
+ content: [
761
+ { type: "text", text: JSON.stringify(finalizedPayload, null, 2) },
762
+ ],
763
+ };
764
+ }
765
+ //# sourceMappingURL=collect_context.js.map