clawvault 1.9.4 → 1.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Observer,
3
3
  parseSessionFile
4
- } from "./chunk-I3UNVUHJ.js";
4
+ } from "./chunk-SIDM2I2C.js";
5
5
 
6
6
  // src/commands/observe.ts
7
7
  import * as fs2 from "fs";
@@ -12,21 +12,25 @@ import { spawn } from "child_process";
12
12
  import * as fs from "fs";
13
13
  import * as path from "path";
14
14
  import chokidar from "chokidar";
15
+ var DEFAULT_FLUSH_THRESHOLD_CHARS = 500;
15
16
  var SessionWatcher = class {
16
17
  watchPath;
17
18
  observer;
18
19
  ignoreInitial;
19
20
  debounceMs;
21
+ flushThresholdChars;
20
22
  watcher = null;
21
23
  fileOffsets = /* @__PURE__ */ new Map();
22
24
  pendingPaths = /* @__PURE__ */ new Set();
23
25
  debounceTimer = null;
24
26
  processingQueue = Promise.resolve();
27
+ bufferedChars = 0;
25
28
  constructor(watchPath, observer, options = {}) {
26
29
  this.watchPath = path.resolve(watchPath);
27
30
  this.observer = observer;
28
31
  this.ignoreInitial = options.ignoreInitial ?? false;
29
32
  this.debounceMs = options.debounceMs ?? 500;
33
+ this.flushThresholdChars = Math.max(1, options.flushThresholdChars ?? DEFAULT_FLUSH_THRESHOLD_CHARS);
30
34
  }
31
35
  async start() {
32
36
  if (!fs.existsSync(this.watchPath)) {
@@ -55,14 +59,22 @@ var SessionWatcher = class {
55
59
  this.watcher?.once("ready", () => resolve3());
56
60
  this.watcher?.once("error", (error) => reject(error));
57
61
  });
62
+ if (this.ignoreInitial) {
63
+ this.primeInitialOffsets();
64
+ }
58
65
  }
59
66
  async stop() {
60
67
  if (this.debounceTimer) {
61
68
  clearTimeout(this.debounceTimer);
62
69
  this.debounceTimer = null;
70
+ this.drainPendingPaths();
63
71
  }
64
- this.pendingPaths.clear();
65
72
  await this.processingQueue.catch(() => void 0);
73
+ if (this.bufferedChars > 0) {
74
+ await this.observer.flush();
75
+ this.bufferedChars = 0;
76
+ }
77
+ this.pendingPaths.clear();
66
78
  await this.watcher?.close();
67
79
  this.watcher = null;
68
80
  }
@@ -72,13 +84,16 @@ var SessionWatcher = class {
72
84
  }
73
85
  this.debounceTimer = setTimeout(() => {
74
86
  this.debounceTimer = null;
75
- const nextPaths = [...this.pendingPaths];
76
- this.pendingPaths.clear();
77
- for (const changedPath of nextPaths) {
78
- this.processingQueue = this.processingQueue.then(() => this.consumeFile(changedPath)).catch(() => void 0);
79
- }
87
+ this.drainPendingPaths();
80
88
  }, this.debounceMs);
81
89
  }
90
+ drainPendingPaths() {
91
+ const nextPaths = [...this.pendingPaths];
92
+ this.pendingPaths.clear();
93
+ for (const changedPath of nextPaths) {
94
+ this.processingQueue = this.processingQueue.then(() => this.consumeFile(changedPath)).catch(() => void 0);
95
+ }
96
+ }
82
97
  async consumeFile(filePath) {
83
98
  const resolved = path.resolve(filePath);
84
99
  if (!fs.existsSync(resolved)) {
@@ -109,6 +124,45 @@ var SessionWatcher = class {
109
124
  return;
110
125
  }
111
126
  await this.observer.processMessages(messages);
127
+ this.bufferedChars += chunk.length;
128
+ if (this.bufferedChars >= this.flushThresholdChars) {
129
+ await this.observer.flush();
130
+ this.bufferedChars = 0;
131
+ }
132
+ }
133
+ primeInitialOffsets() {
134
+ for (const filePath of this.collectFiles(this.watchPath)) {
135
+ try {
136
+ const stats = fs.statSync(filePath);
137
+ if (stats.isFile()) {
138
+ this.fileOffsets.set(filePath, stats.size);
139
+ }
140
+ } catch {
141
+ }
142
+ }
143
+ }
144
+ collectFiles(targetPath) {
145
+ if (!fs.existsSync(targetPath)) {
146
+ return [];
147
+ }
148
+ const resolved = path.resolve(targetPath);
149
+ const stats = fs.statSync(resolved);
150
+ if (stats.isFile()) {
151
+ return [resolved];
152
+ }
153
+ if (!stats.isDirectory()) {
154
+ return [];
155
+ }
156
+ const collected = [];
157
+ for (const entry of fs.readdirSync(resolved, { withFileTypes: true })) {
158
+ const childPath = path.join(resolved, entry.name);
159
+ if (entry.isDirectory()) {
160
+ collected.push(...this.collectFiles(childPath));
161
+ } else if (entry.isFile()) {
162
+ collected.push(path.resolve(childPath));
163
+ }
164
+ }
165
+ return collected;
112
166
  }
113
167
  };
114
168
 
@@ -1,8 +1,9 @@
1
1
  // src/observer/compressor.ts
2
2
  var DATE_HEADING_RE = /^##\s+(\d{4}-\d{2}-\d{2})\s*$/;
3
3
  var OBSERVATION_LINE_RE = /^(🔴|🟡|🟢)\s+(.+)$/u;
4
- var CRITICAL_RE = /(?:\b(?:decision|decided|chose|selected)\s*:|\bdecid(?:e|ed|ing|ion)\b|\berror\b|\bfail(?:ed|ure)?\b|\bprefer(?:ence)?\b|\bblock(?:ed|er)?\b|\bmust\b|\brequired?\b|\burgent\b)/i;
5
- var NOTABLE_RE = /\b(context|pattern|architecture|approach|trade[- ]?off|milestone|notable)\b/i;
4
+ var CRITICAL_RE = /(?:\b(?:decision|decided|chose|chosen|selected|picked|opted|switched to)\s*:?|\bdecid(?:e|ed|ing|ion)\b|\berror\b|\bfail(?:ed|ure|ing)?\b|\bblock(?:ed|er)?\b|\bbreaking(?:\s+change)?s?\b|\bcritical\b|\b\w+\s+chosen\s+(?:for|over|as)\b)/i;
5
+ var DEADLINE_WITH_DATE_RE = /(?:(?:\bdeadline\b|\bdue(?:\s+date)?\b|\bcutoff\b).*(?:\d{4}-\d{2}-\d{2}|\d{1,2}\/\d{1,2}(?:\/\d{2,4})?|(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\s+\d{1,2})|(?:\d{4}-\d{2}-\d{2}|\d{1,2}\/\d{1,2}(?:\/\d{2,4})?|(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]*\s+\d{1,2}).*(?:\bdeadline\b|\bdue(?:\s+date)?\b|\bcutoff\b))/i;
6
+ var NOTABLE_RE = /\b(prefer(?:ence|s)?|likes?|dislikes?|context|pattern|architecture|approach|trade[- ]?off|milestone|stakeholder|teammate|collaborat(?:e|ed|ion)|discussion|notable|deadline|due|timeline)\b/i;
6
7
  var Compressor = class {
7
8
  model;
8
9
  now;
@@ -53,7 +54,12 @@ var Compressor = class {
53
54
  "- Group observations by date heading: ## YYYY-MM-DD",
54
55
  "- Each line must follow: <emoji> <HH:MM> <observation>",
55
56
  "- Priority emojis: \u{1F534} critical, \u{1F7E1} notable, \u{1F7E2} info",
56
- "- Mark decisions, errors, user preferences, and blockers as \u{1F534}",
57
+ "- \u{1F534} for: decisions between alternatives, errors/failures, blockers, deadlines with explicit dates, breaking changes",
58
+ "- \u{1F7E1} for: preferences, architecture discussions, trade-offs, milestones, people interactions, notable context, routine deadlines",
59
+ "- \u{1F7E2} for: routine updates, deployments, builds, general info",
60
+ "- Preferences are \u{1F7E1} unless they indicate a breaking decision between alternatives.",
61
+ "- Each distinct error type or failure must be its own observation line; do not merge different errors.",
62
+ "- If multiple different errors occurred, list each separately with its specific error message.",
57
63
  "- Keep observations concise and factual.",
58
64
  "- Avoid duplicates when possible.",
59
65
  "",
@@ -158,7 +164,7 @@ ${cleaned}`;
158
164
  }
159
165
  /**
160
166
  * Post-process LLM output to enforce priority rules.
161
- * Lines matching CRITICAL_RE get upgraded to 🔴, NOTABLE_RE to 🟡.
167
+ * Lines matching critical rules get upgraded to 🔴, notable rules to 🟡.
162
168
  */
163
169
  enforcePriorityRules(markdown) {
164
170
  return markdown.split(/\r?\n/).map((line) => {
@@ -166,10 +172,10 @@ ${cleaned}`;
166
172
  if (!match) return line;
167
173
  const currentPriority = match[1];
168
174
  const content = match[2];
169
- if (CRITICAL_RE.test(content) && currentPriority !== "\u{1F534}") {
175
+ if (this.isCriticalContent(content) && currentPriority !== "\u{1F534}") {
170
176
  return line.replace(/^🟡|^🟢/u, "\u{1F534}");
171
177
  }
172
- if (NOTABLE_RE.test(content) && currentPriority === "\u{1F7E2}") {
178
+ if (this.isNotableContent(content) && currentPriority === "\u{1F7E2}") {
173
179
  return line.replace(/^🟢/u, "\u{1F7E1}");
174
180
  }
175
181
  return line;
@@ -281,10 +287,16 @@ ${cleaned}`;
281
287
  return chunks.join("\n").trim();
282
288
  }
283
289
  inferPriority(text) {
284
- if (CRITICAL_RE.test(text)) return "\u{1F534}";
285
- if (NOTABLE_RE.test(text)) return "\u{1F7E1}";
290
+ if (this.isCriticalContent(text)) return "\u{1F534}";
291
+ if (this.isNotableContent(text)) return "\u{1F7E1}";
286
292
  return "\u{1F7E2}";
287
293
  }
294
+ isCriticalContent(text) {
295
+ return CRITICAL_RE.test(text) || DEADLINE_WITH_DATE_RE.test(text);
296
+ }
297
+ isNotableContent(text) {
298
+ return NOTABLE_RE.test(text);
299
+ }
288
300
  normalizeText(text) {
289
301
  return text.replace(/\s+/g, " ").replace(/\[[^\]]+\]/g, "").trim().slice(0, 280);
290
302
  }
@@ -58,11 +58,71 @@ function estimateTokens(text) {
58
58
  }
59
59
  return Math.ceil(text.length / 4);
60
60
  }
61
+ function fitWithinBudget(items, budget) {
62
+ if (!Number.isFinite(budget) || budget <= 0) {
63
+ return [];
64
+ }
65
+ const sorted = items.map((item, index) => ({ ...item, index })).sort((a, b) => {
66
+ if (a.priority !== b.priority) {
67
+ return a.priority - b.priority;
68
+ }
69
+ return a.index - b.index;
70
+ });
71
+ let remaining = Math.floor(budget);
72
+ const fitted = [];
73
+ for (const item of sorted) {
74
+ if (!item.text.trim()) {
75
+ continue;
76
+ }
77
+ const cost = estimateTokens(item.text);
78
+ if (cost <= remaining) {
79
+ fitted.push({ text: item.text, source: item.source });
80
+ remaining -= cost;
81
+ }
82
+ if (remaining <= 0) {
83
+ break;
84
+ }
85
+ }
86
+ return fitted;
87
+ }
61
88
 
62
89
  // src/commands/context.ts
63
90
  var DEFAULT_LIMIT = 5;
64
91
  var MAX_SNIPPET_LENGTH = 320;
65
92
  var OBSERVATION_LOOKBACK_DAYS = 7;
93
+ var STOP_WORDS = /* @__PURE__ */ new Set([
94
+ "a",
95
+ "an",
96
+ "and",
97
+ "are",
98
+ "as",
99
+ "at",
100
+ "be",
101
+ "by",
102
+ "for",
103
+ "from",
104
+ "how",
105
+ "in",
106
+ "is",
107
+ "it",
108
+ "of",
109
+ "on",
110
+ "or",
111
+ "that",
112
+ "the",
113
+ "this",
114
+ "to",
115
+ "was",
116
+ "were",
117
+ "what",
118
+ "when",
119
+ "where",
120
+ "who",
121
+ "why",
122
+ "with",
123
+ "you",
124
+ "your"
125
+ ]);
66
126
  function formatRelativeAge(date, now = Date.now()) {
67
127
  const ageMs = Math.max(0, now - date.getTime());
68
128
  const days = Math.floor(ageMs / (24 * 60 * 60 * 1e3));
@@ -97,6 +157,35 @@ function formatContextMarkdown(task, entries) {
97
157
  }
98
158
  return output.trimEnd();
99
159
  }
160
+ function extractKeywords(text) {
161
+ const raw = text.toLowerCase().match(/[a-z0-9]+/g) ?? [];
162
+ const seen = /* @__PURE__ */ new Set();
163
+ const keywords = [];
164
+ for (const token of raw) {
165
+ if (token.length < 2 || STOP_WORDS.has(token) || seen.has(token)) {
166
+ continue;
167
+ }
168
+ seen.add(token);
169
+ keywords.push(token);
170
+ }
171
+ return keywords;
172
+ }
173
+ function computeKeywordOverlapScore(queryKeywords, text) {
174
+ if (queryKeywords.length === 0) {
175
+ return 1;
176
+ }
177
+ const haystack = new Set(extractKeywords(text));
178
+ let matches = 0;
179
+ for (const keyword of queryKeywords) {
180
+ if (haystack.has(keyword)) {
181
+ matches += 1;
182
+ }
183
+ }
184
+ if (matches === 0) {
185
+ return 0.1;
186
+ }
187
+ return matches / queryKeywords.length;
188
+ }
100
189
  function estimateSnippet(source) {
101
190
  const normalized = source.replace(/\s+/g, " ").trim();
102
191
  if (!normalized) {
@@ -134,11 +223,6 @@ function observationPriorityToRank(priority) {
134
223
  if (priority === "\u{1F7E1}") return 4;
135
224
  return 5;
136
225
  }
137
- function observationPriorityScore(priority) {
138
- if (priority === "\u{1F534}") return 1;
139
- if (priority === "\u{1F7E1}") return 0.7;
140
- return 0.4;
141
- }
142
226
  function isLikelyDailyNote(document) {
143
227
  const normalizedPath = document.path.split(path2.sep).join("/").toLowerCase();
144
228
  if (normalizedPath.includes("/daily/")) {
@@ -212,7 +296,7 @@ async function buildDailyContextItems(vault) {
212
296
  }
213
297
  return items;
214
298
  }
215
- function buildObservationContextItems(vaultPath) {
299
+ function buildObservationContextItems(vaultPath, queryKeywords) {
216
300
  const observationMarkdown = readObservations(vaultPath, OBSERVATION_LOOKBACK_DAYS);
217
301
  const parsed = parseObservationLines(observationMarkdown);
218
302
  const items = [];
@@ -227,7 +311,7 @@ function buildObservationContextItems(vaultPath) {
227
311
  title: `${observation.priority} observation (${date})`,
228
312
  path: `observations/${date}.md`,
229
313
  category: "observations",
230
- score: observationPriorityScore(observation.priority),
314
+ score: computeKeywordOverlapScore(queryKeywords, observation.content),
231
315
  snippet,
232
316
  modified: modifiedDate.toISOString(),
233
317
  age: formatRelativeAge(modifiedDate),
@@ -279,21 +363,31 @@ function applyTokenBudget(items, task, budget) {
279
363
  return { context: fullContext, markdown: fullMarkdown };
280
364
  }
281
365
  const normalizedBudget = Math.max(1, Math.floor(budget));
366
+ if (estimateTokens(fullMarkdown) <= normalizedBudget) {
367
+ return { context: fullContext, markdown: fullMarkdown };
368
+ }
282
369
  const header = `## Relevant Context for: ${task}
283
370
 
284
371
  `;
285
- let remaining = normalizedBudget - estimateTokens(header);
286
- const selectedEntries = [];
287
- for (const item of [...items].sort((a, b) => a.priority - b.priority)) {
288
- if (remaining <= 0) {
289
- break;
290
- }
291
- const cost = estimateTokens(renderEntryBlock(item.entry));
292
- if (cost <= remaining) {
293
- selectedEntries.push(item.entry);
294
- remaining -= cost;
295
- }
372
+ const headerCost = estimateTokens(header);
373
+ if (headerCost >= normalizedBudget) {
374
+ return {
375
+ context: [],
376
+ markdown: truncateToBudget(header.trimEnd(), normalizedBudget)
377
+ };
296
378
  }
379
+ const fitted = fitWithinBudget(
380
+ items.map((item, index) => ({
381
+ text: renderEntryBlock(item.entry),
382
+ priority: item.priority,
383
+ source: String(index)
384
+ })),
385
+ normalizedBudget - headerCost
386
+ );
387
+ const selectedEntries = fitted.map((item) => {
388
+ const index = Number.parseInt(item.source, 10);
389
+ return Number.isNaN(index) ? null : items[index]?.entry ?? null;
390
+ }).filter((entry) => Boolean(entry));
297
391
  const markdown = truncateToBudget(formatContextMarkdown(task, selectedEntries), normalizedBudget);
298
392
  return {
299
393
  context: selectedEntries,
@@ -310,20 +404,24 @@ async function buildContext(task, options) {
310
404
  const limit = Math.max(1, options.limit ?? DEFAULT_LIMIT);
311
405
  const recent = options.recent ?? true;
312
406
  const includeObservations = options.includeObservations ?? true;
407
+ const queryKeywords = extractKeywords(normalizedTask);
313
408
  const searchResults = await vault.vsearch(normalizedTask, {
314
409
  limit,
315
410
  temporalBoost: recent
316
411
  });
317
412
  const searchItems = buildSearchContextItems(vault, searchResults);
318
413
  const dailyItems = await buildDailyContextItems(vault);
319
- const observationItems = includeObservations ? buildObservationContextItems(vault.getPath()) : [];
320
- const redObservations = observationItems.filter((item) => item.priority === 1);
321
- const yellowObservations = observationItems.filter((item) => item.priority === 4);
322
- const greenObservations = observationItems.filter((item) => item.priority === 5);
414
+ const observationItems = includeObservations ? buildObservationContextItems(vault.getPath(), queryKeywords) : [];
415
+ const byScoreDesc = (left, right) => right.entry.score - left.entry.score;
416
+ const redObservations = observationItems.filter((item) => item.priority === 1).sort(byScoreDesc);
417
+ const yellowObservations = observationItems.filter((item) => item.priority === 4).sort(byScoreDesc);
418
+ const greenObservations = observationItems.filter((item) => item.priority === 5).sort(byScoreDesc);
419
+ const sortedDailyItems = [...dailyItems].sort(byScoreDesc);
420
+ const sortedSearchItems = [...searchItems].sort(byScoreDesc);
323
421
  const ordered = [
324
422
  ...redObservations,
325
- ...dailyItems,
326
- ...searchItems,
423
+ ...sortedDailyItems,
424
+ ...sortedSearchItems,
327
425
  ...yellowObservations,
328
426
  ...greenObservations
329
427
  ];
@@ -3,7 +3,7 @@ import {
3
3
  contextCommand,
4
4
  formatContextMarkdown,
5
5
  registerContextCommand
6
- } from "../chunk-AVPHNEDB.js";
6
+ } from "../chunk-UBRYOIII.js";
7
7
  import "../chunk-3HFB7EMU.js";
8
8
  import "../chunk-MIIXBNO3.js";
9
9
  export {
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  observeCommand,
3
3
  registerObserveCommand
4
- } from "../chunk-DII3KLBF.js";
5
- import "../chunk-I3UNVUHJ.js";
4
+ } from "../chunk-2MP4EHJ7.js";
5
+ import "../chunk-SIDM2I2C.js";
6
6
  export {
7
7
  observeCommand,
8
8
  registerObserveCommand
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  Observer,
15
15
  parseSessionFile
16
- } from "../chunk-I3UNVUHJ.js";
16
+ } from "../chunk-SIDM2I2C.js";
17
17
 
18
18
  // src/commands/sleep.ts
19
19
  import * as fs from "fs";
@@ -15,6 +15,9 @@ import * as fs from "fs";
15
15
  import * as path from "path";
16
16
  var DEFAULT_HANDOFF_LIMIT = 3;
17
17
  var OBSERVATION_HIGHLIGHT_RE = /^(🔴|🟡)\s+(.+)$/u;
18
+ var MAX_WAKE_RED_OBSERVATIONS = 20;
19
+ var MAX_WAKE_YELLOW_OBSERVATIONS = 10;
20
+ var MAX_WAKE_OUTPUT_LINES = 100;
18
21
  function formatSummaryItems(items, maxItems = 2) {
19
22
  const cleaned = items.map((item) => item.trim()).filter(Boolean);
20
23
  if (cleaned.length === 0) return "";
@@ -60,23 +63,53 @@ function readRecentObservationHighlights(vaultPath) {
60
63
  }
61
64
  return highlights;
62
65
  }
66
+ function timeFromObservationText(text) {
67
+ const match = text.match(/^([01]\d|2[0-3]):([0-5]\d)\b/);
68
+ if (!match) {
69
+ return -1;
70
+ }
71
+ return Number.parseInt(match[1], 10) * 60 + Number.parseInt(match[2], 10);
72
+ }
73
+ function compareByRecency(left, right) {
74
+ if (left.date !== right.date) {
75
+ return right.date.localeCompare(left.date);
76
+ }
77
+ return timeFromObservationText(right.text) - timeFromObservationText(left.text);
78
+ }
63
79
  function formatRecentObservations(highlights) {
64
80
  if (highlights.length === 0) {
65
81
  return "_No critical or notable observations from today or yesterday._";
66
82
  }
83
+ const sorted = [...highlights].sort(compareByRecency);
84
+ const red = sorted.filter((item) => item.priority === "\u{1F534}").slice(0, MAX_WAKE_RED_OBSERVATIONS);
85
+ const yellow = sorted.filter((item) => item.priority === "\u{1F7E1}").slice(0, MAX_WAKE_YELLOW_OBSERVATIONS);
86
+ const visible = [...red, ...yellow].sort(compareByRecency);
87
+ const omittedCount = Math.max(0, highlights.length - visible.length);
67
88
  const byDate = /* @__PURE__ */ new Map();
68
- for (const item of highlights) {
89
+ for (const item of visible) {
69
90
  const bucket = byDate.get(item.date) ?? [];
70
91
  bucket.push(item);
71
92
  byDate.set(item.date, bucket);
72
93
  }
73
94
  const lines = [];
95
+ const bodyLineBudget = Math.max(1, MAX_WAKE_OUTPUT_LINES - (omittedCount > 0 ? 1 : 0));
74
96
  for (const [date, items] of byDate.entries()) {
97
+ if (lines.length >= bodyLineBudget) {
98
+ break;
99
+ }
75
100
  lines.push(`### ${date}`);
76
101
  for (const item of items) {
102
+ if (lines.length >= bodyLineBudget) {
103
+ break;
104
+ }
77
105
  lines.push(`- ${item.priority} ${item.text}`);
78
106
  }
79
- lines.push("");
107
+ if (lines.length < bodyLineBudget) {
108
+ lines.push("");
109
+ }
110
+ }
111
+ if (omittedCount > 0) {
112
+ lines.push(`... and ${omittedCount} more observations (use \`clawvault context\` to query)`);
80
113
  }
81
114
  return lines.join("\n").trim();
82
115
  }
package/dist/index.d.ts CHANGED
@@ -333,7 +333,7 @@ declare class Compressor {
333
333
  private normalizeLlmOutput;
334
334
  /**
335
335
  * Post-process LLM output to enforce priority rules.
336
- * Lines matching CRITICAL_RE get upgraded to 🔴, NOTABLE_RE to 🟡.
336
+ * Lines matching critical rules get upgraded to 🔴, notable rules to 🟡.
337
337
  */
338
338
  private enforcePriorityRules;
339
339
  private fallbackCompression;
@@ -343,6 +343,8 @@ declare class Compressor {
343
343
  private parseSections;
344
344
  private renderSections;
345
345
  private inferPriority;
346
+ private isCriticalContent;
347
+ private isNotableContent;
346
348
  private normalizeText;
347
349
  private extractDate;
348
350
  private extractTime;
@@ -368,22 +370,28 @@ declare class Reflector {
368
370
  interface SessionWatcherOptions {
369
371
  ignoreInitial?: boolean;
370
372
  debounceMs?: number;
373
+ flushThresholdChars?: number;
371
374
  }
372
375
  declare class SessionWatcher {
373
376
  private readonly watchPath;
374
377
  private readonly observer;
375
378
  private readonly ignoreInitial;
376
379
  private readonly debounceMs;
380
+ private readonly flushThresholdChars;
377
381
  private watcher;
378
382
  private fileOffsets;
379
383
  private pendingPaths;
380
384
  private debounceTimer;
381
385
  private processingQueue;
386
+ private bufferedChars;
382
387
  constructor(watchPath: string, observer: Observer, options?: SessionWatcherOptions);
383
388
  start(): Promise<void>;
384
389
  stop(): Promise<void>;
385
390
  private scheduleDrain;
391
+ private drainPendingPaths;
386
392
  private consumeFile;
393
+ private primeInitialOffsets;
394
+ private collectFiles;
387
395
  }
388
396
 
389
397
  declare function parseSessionFile(filePath: string): string[];
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  contextCommand,
17
17
  formatContextMarkdown,
18
18
  registerContextCommand
19
- } from "./chunk-AVPHNEDB.js";
19
+ } from "./chunk-UBRYOIII.js";
20
20
  import {
21
21
  ClawVault,
22
22
  createVault,
@@ -41,13 +41,13 @@ import {
41
41
  SessionWatcher,
42
42
  observeCommand,
43
43
  registerObserveCommand
44
- } from "./chunk-DII3KLBF.js";
44
+ } from "./chunk-2MP4EHJ7.js";
45
45
  import {
46
46
  Compressor,
47
47
  Observer,
48
48
  Reflector,
49
49
  parseSessionFile
50
- } from "./chunk-I3UNVUHJ.js";
50
+ } from "./chunk-SIDM2I2C.js";
51
51
 
52
52
  // src/index.ts
53
53
  import * as fs from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawvault",
3
- "version": "1.9.4",
3
+ "version": "1.9.6",
4
4
  "description": "ClawVault™ - 🐘 An elephant never forgets. Structured memory for OpenClaw agents. Context death resilience, Obsidian-compatible markdown, local semantic search.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",