@letrquan/book 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/CHANGELOG.md +2747 -40
  2. package/README.md +132 -1311
  3. package/dist/additional-roots-H7UNAEHS.js +31 -0
  4. package/dist/{app-MOXXWEOY.js → app-GE4OHJOC.js} +5740 -4045
  5. package/dist/app-GE4OHJOC.js.map +1 -0
  6. package/dist/{chunk-5GDZ22YP.js → chunk-224FJZLH.js} +99 -108
  7. package/dist/chunk-224FJZLH.js.map +1 -0
  8. package/dist/chunk-2AAOF5B5.js +28 -0
  9. package/dist/chunk-2AAOF5B5.js.map +1 -0
  10. package/dist/{chunk-CAGOB2N4.js → chunk-4AS7LKKQ.js} +6 -4
  11. package/dist/chunk-4AS7LKKQ.js.map +1 -0
  12. package/dist/chunk-525CAZPY.js +120 -0
  13. package/dist/chunk-525CAZPY.js.map +1 -0
  14. package/dist/{chunk-URLQAXTR.js → chunk-56SIEMDX.js} +2 -2
  15. package/dist/chunk-56SIEMDX.js.map +1 -0
  16. package/dist/{chunk-XIFFTATU.js → chunk-5YHTKWJN.js} +5420 -4031
  17. package/dist/chunk-5YHTKWJN.js.map +1 -0
  18. package/dist/chunk-62QL5QUP.js +283 -0
  19. package/dist/chunk-62QL5QUP.js.map +1 -0
  20. package/dist/{chunk-KWHQ2XXL.js → chunk-67UZEWWF.js} +3 -3
  21. package/dist/chunk-67UZEWWF.js.map +1 -0
  22. package/dist/chunk-6OQQOMZR.js +785 -0
  23. package/dist/chunk-6OQQOMZR.js.map +1 -0
  24. package/dist/{chunk-GTVB4HEM.js → chunk-AQEQX4FF.js} +344 -315
  25. package/dist/chunk-AQEQX4FF.js.map +1 -0
  26. package/dist/chunk-C7ADUNPR.js +273 -0
  27. package/dist/chunk-C7ADUNPR.js.map +1 -0
  28. package/dist/{chunk-E75IPST5.js → chunk-EFJ2NH6E.js} +12 -58
  29. package/dist/chunk-EFJ2NH6E.js.map +1 -0
  30. package/dist/{chunk-5KLVH3PY.js → chunk-HQI7NYGX.js} +5 -3
  31. package/dist/chunk-HQI7NYGX.js.map +1 -0
  32. package/dist/chunk-IME7R2UI.js +6541 -0
  33. package/dist/chunk-IME7R2UI.js.map +1 -0
  34. package/dist/chunk-J4F2QLM2.js +273 -0
  35. package/dist/chunk-J4F2QLM2.js.map +1 -0
  36. package/dist/{chunk-MAQXCUR4.js → chunk-J62VZYOB.js} +444 -112
  37. package/dist/chunk-J62VZYOB.js.map +1 -0
  38. package/dist/chunk-JTPOI3KU.js +826 -0
  39. package/dist/chunk-JTPOI3KU.js.map +1 -0
  40. package/dist/{chunk-WKBO5R4O.js → chunk-KBQS35OV.js} +2 -2
  41. package/dist/chunk-KBQS35OV.js.map +1 -0
  42. package/dist/chunk-KXAYUIKP.js +90 -0
  43. package/dist/chunk-KXAYUIKP.js.map +1 -0
  44. package/dist/{chunk-VZQXX3WW.js → chunk-QYBD22H5.js} +1 -1
  45. package/dist/chunk-QYBD22H5.js.map +1 -0
  46. package/dist/{chunk-V4BOYR52.js → chunk-RFJ6Q6LE.js} +763 -198
  47. package/dist/chunk-RFJ6Q6LE.js.map +1 -0
  48. package/dist/{chunk-7N4J557C.js → chunk-RT5SATF7.js} +1 -1
  49. package/dist/chunk-RT5SATF7.js.map +1 -0
  50. package/dist/{chunk-C7D4NJ2D.js → chunk-S4JMID4G.js} +68 -26
  51. package/dist/chunk-S4JMID4G.js.map +1 -0
  52. package/dist/{chunk-ANSJPYGI.js → chunk-SOW4WRQP.js} +3 -3
  53. package/dist/chunk-SOW4WRQP.js.map +1 -0
  54. package/dist/{chunk-J7MIZ5CA.js → chunk-TLX3H2CL.js} +518 -79
  55. package/dist/chunk-TLX3H2CL.js.map +1 -0
  56. package/dist/{chunk-DGMQEI6Y.js → chunk-VPNLCMVR.js} +22 -9
  57. package/dist/chunk-VPNLCMVR.js.map +1 -0
  58. package/dist/{chunk-BTZZUCLI.js → chunk-W3XFHEF4.js} +173 -29
  59. package/dist/chunk-W3XFHEF4.js.map +1 -0
  60. package/dist/{chunk-574C6MQR.js → chunk-X4NJT436.js} +130 -60
  61. package/dist/chunk-X4NJT436.js.map +1 -0
  62. package/dist/chunk-X66I2NQO.js +137 -0
  63. package/dist/chunk-X66I2NQO.js.map +1 -0
  64. package/dist/chunk-XHAO3K7I.js +18 -0
  65. package/dist/chunk-XHAO3K7I.js.map +1 -0
  66. package/dist/{chunk-3U5IBM24.js → chunk-Y2VHN7TC.js} +2 -2
  67. package/dist/chunk-Y2VHN7TC.js.map +1 -0
  68. package/dist/chunk-YC6W4EJH.js +947 -0
  69. package/dist/chunk-YC6W4EJH.js.map +1 -0
  70. package/dist/{chunk-24AXE6SP.js → chunk-ZPSZZUWF.js} +1 -1
  71. package/dist/chunk-ZPSZZUWF.js.map +1 -0
  72. package/dist/{command-approvals-6Y57VCWZ.js → command-approvals-VBIU6BLM.js} +6 -5
  73. package/dist/context-CBOBDY5Q.js +38 -0
  74. package/dist/{hook-approvals-CTUFKEJT.js → hook-approvals-ZVT4V7IM.js} +5 -5
  75. package/dist/index.js +996 -155
  76. package/dist/index.js.map +1 -1
  77. package/dist/{interactive-assets-CCAAWVKH.js → interactive-assets-T4I4SEHU.js} +13 -9
  78. package/dist/interactive-assets-T4I4SEHU.js.map +1 -0
  79. package/dist/job-runner.js +159 -111
  80. package/dist/job-runner.js.map +1 -1
  81. package/dist/job-supervisor.d.ts +2 -0
  82. package/dist/job-supervisor.js +112 -0
  83. package/dist/job-supervisor.js.map +1 -0
  84. package/dist/loader-S77OVTHH.js +18 -0
  85. package/dist/{mcp-approvals-WNPH65BK.js → mcp-approvals-HJ2VDYMC.js} +5 -5
  86. package/dist/{mcp-config-YIJIES2C.js → mcp-config-RNB5LEVE.js} +3 -3
  87. package/dist/memory-store-DZUT5HUH.js +75 -0
  88. package/dist/{model-window-store-YFVNXCIP.js → model-window-store-MTSQ5JDJ.js} +8 -4
  89. package/dist/openai-compatible-2OB54YST.js +14 -0
  90. package/dist/{permission-approvals-76PVREFD.js → permission-approvals-YLV6UC5W.js} +5 -5
  91. package/dist/permissions-DHOKF3W2.js +45 -0
  92. package/dist/sandbox-WRYTZH3D.js +41 -0
  93. package/dist/{scrollback-LUKLIBVI.js → scrollback-35ME2GFX.js} +32 -22
  94. package/dist/scrollback-35ME2GFX.js.map +1 -0
  95. package/dist/sdk.d.ts +2219 -2361
  96. package/dist/sdk.js +53 -38
  97. package/dist/sdk.js.map +1 -1
  98. package/dist/settings-loader-JUNPGAAM.js +35 -0
  99. package/dist/settings-loader-JUNPGAAM.js.map +1 -0
  100. package/dist/{shell-selection-V6QAYXPX.js → shell-selection-CDHGJ4GC.js} +2 -2
  101. package/dist/shell-selection-CDHGJ4GC.js.map +1 -0
  102. package/package.json +23 -22
  103. package/dist/app-MOXXWEOY.js.map +0 -1
  104. package/dist/chunk-24AXE6SP.js.map +0 -1
  105. package/dist/chunk-3U5IBM24.js.map +0 -1
  106. package/dist/chunk-574C6MQR.js.map +0 -1
  107. package/dist/chunk-5GDZ22YP.js.map +0 -1
  108. package/dist/chunk-5KLVH3PY.js.map +0 -1
  109. package/dist/chunk-7N4J557C.js.map +0 -1
  110. package/dist/chunk-A7JLBK2Y.js +0 -3506
  111. package/dist/chunk-A7JLBK2Y.js.map +0 -1
  112. package/dist/chunk-ANSJPYGI.js.map +0 -1
  113. package/dist/chunk-BTZZUCLI.js.map +0 -1
  114. package/dist/chunk-C7D4NJ2D.js.map +0 -1
  115. package/dist/chunk-CAGOB2N4.js.map +0 -1
  116. package/dist/chunk-DGMQEI6Y.js.map +0 -1
  117. package/dist/chunk-E75IPST5.js.map +0 -1
  118. package/dist/chunk-EYGD6X2T.js +0 -180
  119. package/dist/chunk-EYGD6X2T.js.map +0 -1
  120. package/dist/chunk-GTVB4HEM.js.map +0 -1
  121. package/dist/chunk-J7MIZ5CA.js.map +0 -1
  122. package/dist/chunk-KWHQ2XXL.js.map +0 -1
  123. package/dist/chunk-L6LK2QIQ.js +0 -657
  124. package/dist/chunk-L6LK2QIQ.js.map +0 -1
  125. package/dist/chunk-MAQXCUR4.js.map +0 -1
  126. package/dist/chunk-S4XL7HOM.js +0 -402
  127. package/dist/chunk-S4XL7HOM.js.map +0 -1
  128. package/dist/chunk-URLQAXTR.js.map +0 -1
  129. package/dist/chunk-V4BOYR52.js.map +0 -1
  130. package/dist/chunk-VZQXX3WW.js.map +0 -1
  131. package/dist/chunk-WKBO5R4O.js.map +0 -1
  132. package/dist/chunk-XIFFTATU.js.map +0 -1
  133. package/dist/context-ZC5IFEAX.js +0 -25
  134. package/dist/interactive-assets-CCAAWVKH.js.map +0 -1
  135. package/dist/loader-4IDS2RIE.js +0 -15
  136. package/dist/openai-compatible-RHZUBHV6.js +0 -10
  137. package/dist/permissions-CHCKTFQQ.js +0 -28
  138. package/dist/sandbox-CNVAQDA7.js +0 -23
  139. package/dist/scrollback-LUKLIBVI.js.map +0 -1
  140. package/dist/settings-loader-M2QP5TX2.js +0 -21
  141. package/patches/ink+6.8.0.patch +0 -13
  142. package/scripts/apply-ink-patch.mjs +0 -29
  143. package/scripts/ink-patch.mjs +0 -59
  144. package/scripts/verify-ink-patch.mjs +0 -11
  145. /package/dist/{command-approvals-6Y57VCWZ.js.map → additional-roots-H7UNAEHS.js.map} +0 -0
  146. /package/dist/{context-ZC5IFEAX.js.map → command-approvals-VBIU6BLM.js.map} +0 -0
  147. /package/dist/{hook-approvals-CTUFKEJT.js.map → context-CBOBDY5Q.js.map} +0 -0
  148. /package/dist/{loader-4IDS2RIE.js.map → hook-approvals-ZVT4V7IM.js.map} +0 -0
  149. /package/dist/{mcp-approvals-WNPH65BK.js.map → loader-S77OVTHH.js.map} +0 -0
  150. /package/dist/{mcp-config-YIJIES2C.js.map → mcp-approvals-HJ2VDYMC.js.map} +0 -0
  151. /package/dist/{model-window-store-YFVNXCIP.js.map → mcp-config-RNB5LEVE.js.map} +0 -0
  152. /package/dist/{openai-compatible-RHZUBHV6.js.map → memory-store-DZUT5HUH.js.map} +0 -0
  153. /package/dist/{permission-approvals-76PVREFD.js.map → model-window-store-MTSQ5JDJ.js.map} +0 -0
  154. /package/dist/{permissions-CHCKTFQQ.js.map → openai-compatible-2OB54YST.js.map} +0 -0
  155. /package/dist/{sandbox-CNVAQDA7.js.map → permission-approvals-YLV6UC5W.js.map} +0 -0
  156. /package/dist/{settings-loader-M2QP5TX2.js.map → permissions-DHOKF3W2.js.map} +0 -0
  157. /package/dist/{shell-selection-V6QAYXPX.js.map → sandbox-WRYTZH3D.js.map} +0 -0
@@ -0,0 +1,785 @@
1
+ import {
2
+ permissionRuleMatchesCall
3
+ } from "./chunk-J62VZYOB.js";
4
+ import {
5
+ parseFrontmatter
6
+ } from "./chunk-KXAYUIKP.js";
7
+ import {
8
+ writeFileAtomic
9
+ } from "./chunk-X4NJT436.js";
10
+ import {
11
+ resolveBookHome
12
+ } from "./chunk-ZPSZZUWF.js";
13
+
14
+ // src/memory-store.ts
15
+ import {
16
+ existsSync,
17
+ lstatSync,
18
+ mkdirSync,
19
+ readdirSync,
20
+ readFileSync,
21
+ renameSync,
22
+ statSync,
23
+ unlinkSync,
24
+ writeFileSync
25
+ } from "fs";
26
+ import { createHash } from "crypto";
27
+ import { basename, isAbsolute, join, relative, resolve } from "path";
28
+
29
+ // src/secret-detect.ts
30
+ var SECRET_PATTERNS = [
31
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----/i,
32
+ /\b(api[_-]?key|token|password|passwd|secret)\s*[:=]\s*\S{8,}/i,
33
+ /https?:\/\/[^\s/]+:[^\s/]+@/i,
34
+ /\bgh[pousr]_[A-Za-z0-9_]{20,}\b/,
35
+ /\bsk-[A-Za-z0-9_-]{20,}\b/
36
+ ];
37
+ function containsSecretPattern(text) {
38
+ return SECRET_PATTERNS.some((re) => re.test(text));
39
+ }
40
+ function looksLikeSecretOrUnfit(text) {
41
+ const trimmed = text.trim();
42
+ if (trimmed.length === 0) return "empty";
43
+ if (SECRET_PATTERNS.some((re) => re.test(trimmed))) return "looks like a secret";
44
+ const longTokens = trimmed.match(/[A-Za-z0-9+/=_-]{48,}/g) ?? [];
45
+ if (longTokens.some((token) => new Set(token).size > 20)) return "contains high-entropy text";
46
+ return null;
47
+ }
48
+
49
+ // src/memory-store.ts
50
+ var MEMORY_TYPES = ["user", "feedback", "project", "reference"];
51
+ var MAX_BODY_CHARS = 1600;
52
+ function sanitizeMemorySlug(rawSlug) {
53
+ const cleaned = rawSlug.replace(/[\x00-\x1f\x7f]/g, "").replace(/[/\\]/g, "").replace(/[[\]()]/g, "").trim().replace(/^\.+/, "").trim();
54
+ return cleaned.length > 0 ? cleaned : null;
55
+ }
56
+ function lastSlugSegment(raw) {
57
+ return raw.split(/[/\\]/).pop() ?? "";
58
+ }
59
+ function memoryFileForSlug(dir, raw) {
60
+ const cleanSlug = sanitizeMemorySlug(lastSlugSegment(raw.trim()));
61
+ if (!cleanSlug) return { error: "Invalid memory slug." };
62
+ const filename = cleanSlug.toLowerCase().endsWith(".md") ? cleanSlug : `${cleanSlug}.md`;
63
+ if (filename.toLowerCase() === INDEX_FILE.toLowerCase()) {
64
+ return { error: "Target filename cannot be the index file." };
65
+ }
66
+ const target = join(dir, filename);
67
+ const rel = relative(dir, target);
68
+ if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) {
69
+ return { error: "Target path must stay inside the project memory directory." };
70
+ }
71
+ return { filename, target };
72
+ }
73
+ function rulesNameMemorySave(rules) {
74
+ return rules.some(
75
+ (rule) => permissionRuleMatchesCall(rule, { id: "memory", name: "MemorySave", arguments: {} })
76
+ );
77
+ }
78
+ function isMemorySaveAvailable(settings) {
79
+ if (!settings?.memory) return true;
80
+ return settings.memory.enabled !== false && settings.memory.autoSave !== false;
81
+ }
82
+ function memoryWriteScope(config) {
83
+ if (!isMemorySaveAvailable(config.settings)) return "none";
84
+ return config.sessionDriver === "agent" ? "learnings" : "full";
85
+ }
86
+ function canModelWriteMemory(config) {
87
+ return memoryWriteScope(config) !== "none";
88
+ }
89
+ var DELEGATED_MEMORY_TYPES = ["project", "reference"];
90
+ function sanitizeMemoryTitle(rawTitle) {
91
+ return rawTitle.replace(/[\x00-\x1f\x7f-\x9f]/g, " ").replace(/[[\]()]/g, "").replace(/^[\s#]+/, "").replace(/\s+/g, " ").trim().slice(0, 120).trim();
92
+ }
93
+ function shouldRejectMemoryText(text) {
94
+ const trimmed = text.trim();
95
+ if (trimmed.length === 0) return "empty";
96
+ if (trimmed.length > MAX_BODY_CHARS * 2) return "too long";
97
+ return looksLikeSecretOrUnfit(trimmed);
98
+ }
99
+ var DEFAULT_MAX_INDEX_LINES = 200;
100
+ var INDEX_FILE = "MEMORY.md";
101
+ var INBOX_DIR = ".inbox";
102
+ var DISCARDED_DIR = "discarded";
103
+ function slugifyWorkspace(workspace) {
104
+ return workspace.replace(/[/\\:]+/g, "-").replace(/[^a-zA-Z0-9._-]/g, "-").replace(/^-+|-+$/g, "") || "workspace";
105
+ }
106
+ function bookRoot(opts) {
107
+ return opts?.bookRoot ?? resolveBookHome();
108
+ }
109
+ function getProjectMemoryDir(workspace, opts) {
110
+ return join(bookRoot(opts), "projects", slugifyWorkspace(workspace), "memory");
111
+ }
112
+ function getMemoryInboxDir(workspace, opts) {
113
+ return opts?.dir ? join(opts.dir, INBOX_DIR) : join(getProjectMemoryDir(workspace, opts), INBOX_DIR);
114
+ }
115
+ function getMemoryExtractionStatePath(workspace, opts) {
116
+ return join(getProjectMemoryDir(workspace, opts), ".extraction-state.json");
117
+ }
118
+ function getMemoryExtractionLockPath(workspace, opts) {
119
+ return join(getProjectMemoryDir(workspace, opts), ".extraction.lock");
120
+ }
121
+ function isMemoryType(value) {
122
+ return typeof value === "string" && MEMORY_TYPES.includes(value);
123
+ }
124
+ function isMemoryStatus(value) {
125
+ return value === "approved" || value === "pending" || value === "discarded" || value === "superseded";
126
+ }
127
+ function titleFromBody(body) {
128
+ const firstHeading = body.split("\n").find((line) => line.startsWith("# "));
129
+ return firstHeading?.replace(/^#\s+/, "").trim();
130
+ }
131
+ function summarizeMemoryFile(path, name = basename(path)) {
132
+ try {
133
+ const st = statSync(path);
134
+ if (!st.isFile()) return null;
135
+ const raw = readFileSync(path, "utf-8");
136
+ const { body, frontmatter } = parseFrontmatter(raw);
137
+ const proposedTitle = typeof frontmatter.proposedTitle === "string" ? frontmatter.proposedTitle : void 0;
138
+ const title = proposedTitle ?? (typeof frontmatter.title === "string" ? frontmatter.title : void 0) ?? titleFromBody(body);
139
+ return {
140
+ name,
141
+ path,
142
+ type: isMemoryType(frontmatter.type) ? frontmatter.type : void 0,
143
+ status: isMemoryStatus(frontmatter.status) ? frontmatter.status : void 0,
144
+ title,
145
+ created: typeof frontmatter.created === "string" ? frontmatter.created : void 0,
146
+ updated: typeof frontmatter.updated === "string" ? frontmatter.updated : void 0,
147
+ size: st.size,
148
+ ...frontmatter.driver === "agent" ? { driver: "agent" } : {}
149
+ };
150
+ } catch {
151
+ return null;
152
+ }
153
+ }
154
+ function listMemoryFiles(workspace, opts) {
155
+ const dir = getProjectMemoryDir(workspace, opts);
156
+ if (!existsSync(dir)) return [];
157
+ const files = [];
158
+ try {
159
+ for (const entry of readdirSync(dir).sort()) {
160
+ if (!entry.toLowerCase().endsWith(".md")) continue;
161
+ if (entry.toLowerCase() === INDEX_FILE.toLowerCase()) continue;
162
+ const full = join(dir, entry);
163
+ const summary = summarizeMemoryFile(full, entry);
164
+ if (summary && summary.status !== "superseded") files.push(summary);
165
+ }
166
+ } catch {
167
+ return files;
168
+ }
169
+ return files;
170
+ }
171
+ function listMemoryCandidates(workspace, opts) {
172
+ const dir = getMemoryInboxDir(workspace, opts);
173
+ if (!existsSync(dir)) return [];
174
+ const files = [];
175
+ try {
176
+ for (const entry of readdirSync(dir).sort()) {
177
+ if (!entry.endsWith(".md")) continue;
178
+ const full = join(dir, entry);
179
+ const summary = summarizeMemoryFile(full, entry);
180
+ if (summary) files.push(summary);
181
+ }
182
+ } catch {
183
+ return files;
184
+ }
185
+ return files;
186
+ }
187
+ function countMemoryCandidates(workspace, opts) {
188
+ const dir = getMemoryInboxDir(workspace, opts);
189
+ if (!existsSync(dir)) return 0;
190
+ try {
191
+ return readdirSync(dir).filter((entry) => !entry.startsWith(".") && entry.endsWith(".md")).length;
192
+ } catch {
193
+ return 0;
194
+ }
195
+ }
196
+ function readFirstLines(path, maxLines) {
197
+ const raw = readFileSync(path, "utf-8");
198
+ const lines = raw.split("\n");
199
+ const loaded = lines.slice(0, maxLines);
200
+ return {
201
+ text: loaded.join("\n").trim(),
202
+ totalLines: lines.filter((line) => line.trim().length > 0).length,
203
+ loadedLines: loaded.filter((line) => line.trim().length > 0).length
204
+ };
205
+ }
206
+ function loadMemoryContext(workspace, opts) {
207
+ const dir = getProjectMemoryDir(workspace, opts);
208
+ const maxIndexLines = opts?.maxIndexLines ?? DEFAULT_MAX_INDEX_LINES;
209
+ const result = {
210
+ dir,
211
+ indexFile: null,
212
+ indexLoaded: false,
213
+ indexLineCount: 0,
214
+ loadedLineCount: 0,
215
+ indexText: "",
216
+ files: [],
217
+ candidates: []
218
+ };
219
+ if (!existsSync(dir)) return result;
220
+ const indexFile = join(dir, INDEX_FILE);
221
+ if (existsSync(indexFile)) {
222
+ try {
223
+ const st = statSync(indexFile);
224
+ if (st.isFile()) {
225
+ const index = readFirstLines(indexFile, maxIndexLines);
226
+ result.indexFile = indexFile;
227
+ result.indexLoaded = index.text.length > 0;
228
+ result.indexLineCount = index.totalLines;
229
+ result.loadedLineCount = index.loadedLines;
230
+ result.indexText = index.text;
231
+ }
232
+ } catch {
233
+ result.indexFile = indexFile;
234
+ }
235
+ }
236
+ result.files = listMemoryFiles(workspace, opts);
237
+ result.candidates = listMemoryCandidates(workspace, opts);
238
+ return result;
239
+ }
240
+ function getNewestMemoryWriteTime(memoryDir) {
241
+ if (!existsSync(memoryDir)) return null;
242
+ let newestMs = 0;
243
+ function scanEntries(dir, isInbox) {
244
+ let entries;
245
+ try {
246
+ entries = readdirSync(dir);
247
+ } catch {
248
+ return;
249
+ }
250
+ for (const entry of entries) {
251
+ if (entry.startsWith(".")) {
252
+ if (!isInbox && entry === INBOX_DIR) {
253
+ scanEntries(join(dir, entry), true);
254
+ }
255
+ continue;
256
+ }
257
+ if (isInbox && entry === DISCARDED_DIR) {
258
+ continue;
259
+ }
260
+ const full = join(dir, entry);
261
+ try {
262
+ const st = lstatSync(full);
263
+ if (st.isSymbolicLink()) continue;
264
+ if (st.isFile()) {
265
+ if (st.mtimeMs > newestMs) newestMs = st.mtimeMs;
266
+ }
267
+ } catch {
268
+ }
269
+ }
270
+ }
271
+ scanEntries(memoryDir, false);
272
+ return newestMs > 0 ? new Date(newestMs) : null;
273
+ }
274
+ function getMemoryHealth(workspaceOrContext, opts) {
275
+ const dir = typeof workspaceOrContext === "string" ? opts?.dir ?? getProjectMemoryDir(workspaceOrContext, opts) : workspaceOrContext.dir;
276
+ const workspace = typeof workspaceOrContext === "string" ? workspaceOrContext : "";
277
+ let approvedCount = 0;
278
+ let supersededCount = 0;
279
+ if (existsSync(dir)) {
280
+ try {
281
+ for (const entry of readdirSync(dir)) {
282
+ if (!entry.toLowerCase().endsWith(".md") || entry.toLowerCase() === INDEX_FILE.toLowerCase())
283
+ continue;
284
+ const full = join(dir, entry);
285
+ try {
286
+ const st = lstatSync(full);
287
+ if (st.isSymbolicLink() || !st.isFile()) continue;
288
+ const summary = summarizeMemoryFile(full, entry);
289
+ if (summary && (summary.status === "approved" || summary.status === void 0)) {
290
+ approvedCount++;
291
+ } else if (summary?.status === "superseded") {
292
+ supersededCount++;
293
+ }
294
+ } catch {
295
+ }
296
+ }
297
+ } catch {
298
+ }
299
+ }
300
+ const inboxCount = countMemoryCandidates(workspace, { ...opts, dir });
301
+ let indexLineCount = 0;
302
+ try {
303
+ indexLineCount = readIndexLines(dir).length;
304
+ } catch {
305
+ }
306
+ const lastWrite = getNewestMemoryWriteTime(dir);
307
+ return {
308
+ approvedCount,
309
+ supersededCount,
310
+ inboxCount,
311
+ indexLineCount,
312
+ lastWrite
313
+ };
314
+ }
315
+ function safeTitle(title) {
316
+ return title.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "memory";
317
+ }
318
+ function shortHash(input) {
319
+ return createHash("sha256").update(input).digest("hex").slice(0, 8);
320
+ }
321
+ function formatTags(tags) {
322
+ if (!tags?.length) return "";
323
+ return `tags:
324
+ ${tags.map((tag) => `- ${tag}`).join("\n")}
325
+ `;
326
+ }
327
+ function renderMemoryMarkdown(input, status, now) {
328
+ const created = input.created ?? now.toISOString();
329
+ const updated = now.toISOString();
330
+ const sanitizedTitle = sanitizeMemoryTitle(input.title);
331
+ const title = status === "pending" ? `Candidate: ${sanitizedTitle}` : sanitizedTitle;
332
+ const origin = input.origin ?? "user-text";
333
+ const source = input.source ?? (origin === "user-text" ? "manual" : "auto");
334
+ const externalContext = input.externalContext ?? false;
335
+ const fm = [
336
+ `type: ${input.type}`,
337
+ `status: ${status}`,
338
+ `origin: ${origin}`,
339
+ `source: ${source}`,
340
+ `externalContext: ${externalContext}`,
341
+ `created: ${created}`,
342
+ `updated: ${updated}`
343
+ ];
344
+ if (input.sessionId) fm.push(`sessionId: ${input.sessionId}`);
345
+ if (input.driver === "agent") fm.push("driver: agent");
346
+ if (input.evidence?.length) {
347
+ fm.push(`evidence:
348
+ ${input.evidence.map((e) => `- ${e}`).join("\n")}`);
349
+ }
350
+ if (input.targetSlug) fm.push(`targetSlug: ${input.targetSlug}`);
351
+ if (input.supersedes) fm.push(`supersedes: ${input.supersedes}`);
352
+ if (input.supersededBy) fm.push(`supersededBy: ${input.supersededBy}`);
353
+ if ("confidence" in input && input.confidence) fm.push(`confidence: ${input.confidence}`);
354
+ if (status === "pending") fm.push(`proposedTitle: ${sanitizedTitle}`);
355
+ if (input.tags?.length) {
356
+ fm.push(formatTags(input.tags).trimEnd());
357
+ }
358
+ return ["---", ...fm, "---", "", `# ${title}`, "", input.body.trim(), ""].join("\n");
359
+ }
360
+ function writeMemoryCandidate(workspace, candidate, opts) {
361
+ try {
362
+ const title = sanitizeMemoryTitle(candidate.title);
363
+ if (!title) {
364
+ return { ok: false, error: "title must not be empty after sanitizing." };
365
+ }
366
+ const now = opts?.now ?? /* @__PURE__ */ new Date();
367
+ const inbox = getMemoryInboxDir(workspace, opts);
368
+ mkdirSync(inbox, { recursive: true });
369
+ const stamp = now.toISOString().replace(/[-:.]/g, "").slice(0, 15);
370
+ const hash = shortHash(`${candidate.type}
371
+ ${title}
372
+ ${candidate.body}
373
+ ${now.toISOString()}`);
374
+ const filename = `${stamp}-cand-${safeTitle(title)}-${hash}.md`;
375
+ const path = join(inbox, filename);
376
+ writeFileSync(path, renderMemoryMarkdown({ ...candidate, title }, "pending", now), "utf-8");
377
+ return { ok: true, path };
378
+ } catch (e) {
379
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
380
+ }
381
+ }
382
+ function resolveCandidatePath(workspace, candidateFile, opts) {
383
+ const inbox = resolve(getMemoryInboxDir(workspace, opts));
384
+ const raw = isAbsolute(candidateFile) ? candidateFile : join(inbox, candidateFile);
385
+ const resolved = resolve(raw);
386
+ const rel = relative(inbox, resolved);
387
+ if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) return null;
388
+ return resolved;
389
+ }
390
+ function activeMemoryFilename(input, now) {
391
+ const date = now.toISOString().slice(0, 10);
392
+ const hash = shortHash(`${input.type}
393
+ ${input.title}
394
+ ${input.body}`);
395
+ return `${date}-${input.type}-${safeTitle(input.title)}-${hash}.md`;
396
+ }
397
+ function frontmatterSlug(raw) {
398
+ return raw?.trim() ? sanitizeMemorySlug(lastSlugSegment(raw.trim())) ?? void 0 : void 0;
399
+ }
400
+ function readMemoryFile(path) {
401
+ try {
402
+ const raw = readFileSync(path, "utf-8");
403
+ const { body, frontmatter } = parseFrontmatter(raw);
404
+ if (!isMemoryType(frontmatter.type)) return null;
405
+ const rawProposed = typeof frontmatter.proposedTitle === "string" ? frontmatter.proposedTitle : void 0;
406
+ const proposedTitle = rawProposed ? sanitizeMemoryTitle(rawProposed) || void 0 : void 0;
407
+ const heading = titleFromBody(body)?.replace(/^Candidate:\s*/i, "");
408
+ const title = proposedTitle || (typeof frontmatter.title === "string" ? sanitizeMemoryTitle(frontmatter.title) : "") || (heading ? sanitizeMemoryTitle(heading) : "") || basename(path, ".md");
409
+ const tags = Array.isArray(frontmatter.tags) ? frontmatter.tags.filter((t) => typeof t === "string") : void 0;
410
+ const evidence = Array.isArray(frontmatter.evidence) ? frontmatter.evidence.filter((e) => typeof e === "string") : void 0;
411
+ const cleanBody = body.replace(/^#\s+[^\n]*\n+/i, "").trim() || body.trim();
412
+ const origin = frontmatter.origin === "model-tool" || frontmatter.origin === "extraction" || frontmatter.origin === "user-text" ? frontmatter.origin : "user-text";
413
+ const source = frontmatter.source === "manual" || frontmatter.source === "auto" ? frontmatter.source : origin === "user-text" ? "manual" : "auto";
414
+ const externalContext = frontmatter.externalContext === true || frontmatter.externalContext === "true";
415
+ const sessionId = typeof frontmatter.sessionId === "string" ? frontmatter.sessionId : void 0;
416
+ const rawTargetSlug = typeof frontmatter.targetSlug === "string" ? frontmatter.targetSlug : void 0;
417
+ const targetSlug = frontmatterSlug(rawTargetSlug);
418
+ const status = isMemoryStatus(frontmatter.status) ? frontmatter.status : void 0;
419
+ const created = typeof frontmatter.created === "string" ? frontmatter.created : void 0;
420
+ const updated = typeof frontmatter.updated === "string" ? frontmatter.updated : void 0;
421
+ const supersedes = frontmatterSlug(
422
+ typeof frontmatter.supersedes === "string" ? frontmatter.supersedes : void 0
423
+ );
424
+ return {
425
+ supersedes,
426
+ type: frontmatter.type,
427
+ title,
428
+ body: cleanBody,
429
+ origin,
430
+ source,
431
+ externalContext,
432
+ sessionId,
433
+ evidence,
434
+ targetSlug,
435
+ status,
436
+ confidence: frontmatter.confidence === "low" || frontmatter.confidence === "medium" || frontmatter.confidence === "high" ? frontmatter.confidence : void 0,
437
+ tags,
438
+ created,
439
+ updated,
440
+ ...frontmatter.driver === "agent" ? { driver: "agent" } : {}
441
+ };
442
+ } catch {
443
+ return null;
444
+ }
445
+ }
446
+ function parseCandidateFile(path) {
447
+ return readMemoryFile(path);
448
+ }
449
+ var INDEX_UNSAFE = /[\x00-\x1f\x7f-\x9f[\]()<>]/g;
450
+ function indexHook(body) {
451
+ const first = body.trim().split("\n")[0]?.replace(INDEX_UNSAFE, " ").replace(/\s+/g, " ").trim() ?? "";
452
+ return first.length > 100 ? `${first.slice(0, 100)}\u2026` : first;
453
+ }
454
+ var sameFile = (a, b) => a.toLowerCase() === b.toLowerCase();
455
+ function indexEntryFile(line) {
456
+ return /^- \[[^\]]*\]\(([^()]+)\)/.exec(line)?.[1];
457
+ }
458
+ function readIndexLines(dir) {
459
+ const indexPath = join(dir, INDEX_FILE);
460
+ if (!existsSync(indexPath)) return [];
461
+ return readFileSync(indexPath, "utf-8").split("\n").filter((line) => line.trim().length > 0);
462
+ }
463
+ function writeIndexLines(dir, lines) {
464
+ writeFileAtomic(
465
+ join(dir, INDEX_FILE),
466
+ lines.length > 0 ? lines.join("\n") + "\n" : "# Book memory index\n"
467
+ );
468
+ }
469
+ function liveIndexLines(dir, lines, drop) {
470
+ return lines.filter((line) => {
471
+ const file = indexEntryFile(line);
472
+ if (!file) return true;
473
+ if (drop.some((name) => sameFile(name, file))) return false;
474
+ const path = join(dir, file);
475
+ if (!existsSync(path)) return false;
476
+ return summarizeMemoryFile(path, file)?.status !== "superseded";
477
+ });
478
+ }
479
+ function removeIndexEntry(dir, filename) {
480
+ if (!existsSync(join(dir, INDEX_FILE))) return;
481
+ writeIndexLines(dir, liveIndexLines(dir, readIndexLines(dir), [filename]));
482
+ }
483
+ function updateMemoryIndex(dir, title, filename, type, now, body, supersedes) {
484
+ const hook = indexHook(body);
485
+ const entry = `- [${title}](${filename}) \u2014 ${type} \u2014 ${now.toISOString().slice(0, 10)}${hook && hook !== title ? ` \u2014 ${hook}` : ""}`;
486
+ const drop = supersedes ? [filename, supersedes] : [filename];
487
+ let lines = liveIndexLines(dir, readIndexLines(dir), drop);
488
+ if (lines.length === 0) {
489
+ lines = ["# Book memory index", entry];
490
+ } else if (lines[0].startsWith("#")) {
491
+ lines = [lines[0], entry, ...lines.slice(1)];
492
+ } else {
493
+ lines = [entry, ...lines];
494
+ }
495
+ writeIndexLines(dir, lines);
496
+ return { entry, lineCount: lines.length };
497
+ }
498
+ function setFrontmatterFields(raw, fields) {
499
+ const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(raw);
500
+ const entries = Object.entries(fields);
501
+ if (!match) {
502
+ return `---
503
+ ${entries.map(([k, v]) => `${k}: ${v}`).join("\n")}
504
+ ---
505
+
506
+ ${raw}`;
507
+ }
508
+ let frontmatter = match[1];
509
+ for (const [key, value] of entries) {
510
+ const line = new RegExp(`^${key}:.*$`, "m");
511
+ frontmatter = line.test(frontmatter) ? frontmatter.replace(line, `${key}: ${value}`) : `${frontmatter}
512
+ ${key}: ${value}`;
513
+ }
514
+ return `---
515
+ ${frontmatter}
516
+ ---
517
+ ${raw.slice(match[0].length)}`;
518
+ }
519
+ function markSuperseded(dir, oldFilename, byFilename, now) {
520
+ try {
521
+ const target = join(dir, oldFilename);
522
+ if (isSymlink(target)) return;
523
+ const raw = readFileSync(target, "utf-8");
524
+ writeFileAtomic(
525
+ target,
526
+ setFrontmatterFields(raw, {
527
+ status: "superseded",
528
+ supersededBy: byFilename,
529
+ updated: now.toISOString()
530
+ })
531
+ );
532
+ } catch {
533
+ }
534
+ }
535
+ function resolveSupersedes(dir, raw, own) {
536
+ if (!raw?.trim()) return {};
537
+ const named = memoryFileForSlug(dir, raw);
538
+ if ("error" in named) return { error: `supersedes: ${named.error}` };
539
+ let actual;
540
+ try {
541
+ actual = readdirSync(dir).find((f) => sameFile(f, named.filename));
542
+ } catch {
543
+ actual = void 0;
544
+ }
545
+ if (!actual || isSymlink(join(dir, actual))) {
546
+ return {
547
+ error: `supersedes: no memory file ${named.filename}. Use the file name from <memory-index>.`
548
+ };
549
+ }
550
+ if (own && sameFile(actual, own)) return {};
551
+ const current = summarizeMemoryFile(join(dir, actual), actual);
552
+ if (current?.status === "superseded") {
553
+ return {
554
+ error: `supersedes: ${actual} is already superseded; supersede its replacement instead.`
555
+ };
556
+ }
557
+ return { filename: actual };
558
+ }
559
+ function isSymlink(path) {
560
+ try {
561
+ return lstatSync(path).isSymbolicLink();
562
+ } catch {
563
+ return false;
564
+ }
565
+ }
566
+ function approvedTarget(dir, input, slug, now) {
567
+ const named = slug?.trim() ? memoryFileForSlug(dir, slug) : null;
568
+ if (named && "error" in named) return { error: named.error };
569
+ const filename = named?.filename ?? activeMemoryFilename(input, now);
570
+ const target = named?.target ?? join(dir, filename);
571
+ if (isSymlink(target)) return { error: "Refusing to overwrite symlinked memory file." };
572
+ const existingCreated = named && existsSync(target) ? readMemoryFile(target)?.created : void 0;
573
+ return { filename, target, existingCreated };
574
+ }
575
+ function saveMemory(workspace, candidate, opts) {
576
+ const quarantineExternal = opts?.quarantineExternal ?? true;
577
+ const isQuarantined = candidate.externalContext === true && quarantineExternal;
578
+ const requireApproval = (opts?.requireApproval ?? false) || isQuarantined;
579
+ const title = sanitizeMemoryTitle(candidate.title);
580
+ if (!title) {
581
+ return {
582
+ ok: false,
583
+ error: "title must not be empty after sanitizing.",
584
+ status: requireApproval ? "pending" : "approved",
585
+ quarantined: isQuarantined
586
+ };
587
+ }
588
+ if (requireApproval) {
589
+ const rawSlug = opts?.slug?.trim() ? opts.slug : candidate.targetSlug?.trim() ? candidate.targetSlug : void 0;
590
+ let targetSlug;
591
+ if (rawSlug) {
592
+ const named = memoryFileForSlug(getProjectMemoryDir(workspace, opts), rawSlug);
593
+ if ("error" in named) {
594
+ return { ok: false, error: named.error, status: "pending", quarantined: isQuarantined };
595
+ }
596
+ targetSlug = named.filename;
597
+ }
598
+ const retire = resolveSupersedes(
599
+ getProjectMemoryDir(workspace, opts),
600
+ candidate.supersedes,
601
+ targetSlug
602
+ );
603
+ if ("error" in retire) {
604
+ return { ok: false, error: retire.error, status: "pending", quarantined: isQuarantined };
605
+ }
606
+ const candidateInput = {
607
+ ...candidate,
608
+ title,
609
+ targetSlug,
610
+ supersedes: retire.filename
611
+ };
612
+ const writeResult = writeMemoryCandidate(workspace, candidateInput, opts);
613
+ if (!writeResult.ok) {
614
+ return { ok: false, error: writeResult.error, status: "pending", quarantined: isQuarantined };
615
+ }
616
+ return { ok: true, path: writeResult.path, status: "pending", quarantined: isQuarantined };
617
+ }
618
+ try {
619
+ const now = opts?.now ?? /* @__PURE__ */ new Date();
620
+ const dir = getProjectMemoryDir(workspace, opts);
621
+ mkdirSync(dir, { recursive: true });
622
+ const placed = approvedTarget(dir, { ...candidate, title }, opts?.slug, now);
623
+ if ("error" in placed) return { ok: false, error: placed.error, status: "approved" };
624
+ const { filename, target, existingCreated } = placed;
625
+ const retire = resolveSupersedes(dir, candidate.supersedes, filename);
626
+ if ("error" in retire) return { ok: false, error: retire.error, status: "approved" };
627
+ readIndexLines(dir);
628
+ const memoryInput = {
629
+ ...candidate,
630
+ title,
631
+ supersedes: retire.filename,
632
+ created: existingCreated ?? candidate.created
633
+ };
634
+ writeFileSync(target, renderMemoryMarkdown(memoryInput, "approved", now), "utf-8");
635
+ const index = updateMemoryIndex(
636
+ dir,
637
+ title,
638
+ filename,
639
+ candidate.type,
640
+ now,
641
+ candidate.body,
642
+ memoryInput.supersedes
643
+ );
644
+ if (memoryInput.supersedes) markSuperseded(dir, memoryInput.supersedes, filename, now);
645
+ return {
646
+ ok: true,
647
+ path: target,
648
+ indexLine: index.entry,
649
+ indexLineCount: index.lineCount,
650
+ retired: memoryInput.supersedes,
651
+ status: "approved"
652
+ };
653
+ } catch (e) {
654
+ return { ok: false, error: e instanceof Error ? e.message : String(e), status: "approved" };
655
+ }
656
+ }
657
+ function memoryEntryDriver(workspace, slug, opts) {
658
+ const named = memoryFileForSlug(getProjectMemoryDir(workspace, opts), slug);
659
+ if ("error" in named || !existsSync(named.target)) return null;
660
+ return readMemoryFile(named.target)?.driver === "agent" ? "agent" : "human";
661
+ }
662
+ function deleteMemoryEntry(workspace, slug, opts) {
663
+ try {
664
+ const dir = getProjectMemoryDir(workspace, opts);
665
+ const named = memoryFileForSlug(dir, slug);
666
+ if ("error" in named) return { ok: false, error: named.error };
667
+ const { filename, target } = named;
668
+ if (!existsSync(target)) {
669
+ return { ok: false, error: `Memory file not found: ${filename}` };
670
+ }
671
+ if (lstatSync(target).isSymbolicLink()) {
672
+ return { ok: false, error: "Refusing to delete symlinked file." };
673
+ }
674
+ unlinkSync(target);
675
+ try {
676
+ removeIndexEntry(dir, filename);
677
+ } catch {
678
+ }
679
+ return { ok: true, path: target };
680
+ } catch (e) {
681
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
682
+ }
683
+ }
684
+ function approveMemoryCandidate(workspace, candidateFile, opts) {
685
+ try {
686
+ const candidatePath = resolveCandidatePath(workspace, candidateFile, opts);
687
+ if (!candidatePath)
688
+ return { ok: false, error: "Candidate path must stay inside the memory inbox." };
689
+ if (!existsSync(candidatePath))
690
+ return { ok: false, error: `Candidate not found: ${candidateFile}` };
691
+ if (lstatSync(candidatePath).isSymbolicLink())
692
+ return { ok: false, error: "Refusing to approve symlinked candidate." };
693
+ const candidate = parseCandidateFile(candidatePath);
694
+ if (!candidate)
695
+ return { ok: false, error: "Candidate has invalid or missing memory frontmatter." };
696
+ const now = opts?.now ?? /* @__PURE__ */ new Date();
697
+ const dir = getProjectMemoryDir(workspace, opts);
698
+ mkdirSync(dir, { recursive: true });
699
+ if (!candidate.title.trim()) return { ok: false, error: "Candidate has an empty title." };
700
+ const placed = approvedTarget(dir, candidate, candidate.targetSlug, now);
701
+ if ("error" in placed) return { ok: false, error: placed.error };
702
+ const { filename, target, existingCreated } = placed;
703
+ const retire = resolveSupersedes(dir, candidate.supersedes, filename);
704
+ const supersedes = "error" in retire ? void 0 : retire.filename;
705
+ const retireSkipped = "error" in retire ? retire.error : void 0;
706
+ const memoryInput = {
707
+ ...candidate,
708
+ supersedes,
709
+ created: existingCreated ?? candidate.created
710
+ };
711
+ const rendered = renderMemoryMarkdown(memoryInput, "approved", now);
712
+ readIndexLines(dir);
713
+ renameSync(candidatePath, join(getDiscardedDir(workspace, opts), basename(candidatePath)));
714
+ writeFileSync(target, rendered, "utf-8");
715
+ updateMemoryIndex(
716
+ dir,
717
+ candidate.title,
718
+ filename,
719
+ candidate.type,
720
+ now,
721
+ candidate.body,
722
+ supersedes
723
+ );
724
+ if (supersedes) markSuperseded(dir, supersedes, filename, now);
725
+ return { ok: true, path: target, retired: supersedes, retireSkipped };
726
+ } catch (e) {
727
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
728
+ }
729
+ }
730
+ function getDiscardedDir(workspace, opts) {
731
+ const dir = join(getMemoryInboxDir(workspace, opts), DISCARDED_DIR);
732
+ mkdirSync(dir, { recursive: true });
733
+ return dir;
734
+ }
735
+ function discardMemoryCandidate(workspace, candidateFile, opts) {
736
+ try {
737
+ const candidatePath = resolveCandidatePath(workspace, candidateFile, opts);
738
+ if (!candidatePath)
739
+ return { ok: false, error: "Candidate path must stay inside the memory inbox." };
740
+ if (!existsSync(candidatePath))
741
+ return { ok: false, error: `Candidate not found: ${candidateFile}` };
742
+ if (lstatSync(candidatePath).isSymbolicLink())
743
+ return { ok: false, error: "Refusing to discard symlinked candidate." };
744
+ const discardedPath = join(getDiscardedDir(workspace, opts), basename(candidatePath));
745
+ renameSync(candidatePath, discardedPath);
746
+ return { ok: true, path: discardedPath };
747
+ } catch (e) {
748
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
749
+ }
750
+ }
751
+
752
+ export {
753
+ containsSecretPattern,
754
+ MEMORY_TYPES,
755
+ MAX_BODY_CHARS,
756
+ sanitizeMemorySlug,
757
+ memoryFileForSlug,
758
+ rulesNameMemorySave,
759
+ isMemorySaveAvailable,
760
+ memoryWriteScope,
761
+ canModelWriteMemory,
762
+ DELEGATED_MEMORY_TYPES,
763
+ sanitizeMemoryTitle,
764
+ shouldRejectMemoryText,
765
+ DEFAULT_MAX_INDEX_LINES,
766
+ slugifyWorkspace,
767
+ getProjectMemoryDir,
768
+ getMemoryInboxDir,
769
+ getMemoryExtractionStatePath,
770
+ getMemoryExtractionLockPath,
771
+ listMemoryFiles,
772
+ listMemoryCandidates,
773
+ countMemoryCandidates,
774
+ loadMemoryContext,
775
+ getNewestMemoryWriteTime,
776
+ getMemoryHealth,
777
+ writeMemoryCandidate,
778
+ readMemoryFile,
779
+ saveMemory,
780
+ memoryEntryDriver,
781
+ deleteMemoryEntry,
782
+ approveMemoryCandidate,
783
+ discardMemoryCandidate
784
+ };
785
+ //# sourceMappingURL=chunk-6OQQOMZR.js.map