clawvault 1.11.0 → 1.11.2
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.
- package/dist/{chunk-5VYRT2XZ.js → chunk-GZOBCUVO.js} +1 -1
- package/dist/{chunk-RPMQZZPE.js → chunk-KOF3HYSL.js} +74 -12
- package/dist/commands/observe.js +2 -2
- package/dist/commands/sleep.js +1 -1
- package/dist/commands/wake.d.ts +2 -0
- package/dist/commands/wake.js +78 -8
- package/dist/index.js +2 -2
- package/package.json +1 -1
|
@@ -34,6 +34,7 @@ var Compressor = class {
|
|
|
34
34
|
return this.mergeObservations(existingObservations, fallback);
|
|
35
35
|
}
|
|
36
36
|
resolveProvider() {
|
|
37
|
+
if (process.env.CLAWVAULT_NO_LLM) return null;
|
|
37
38
|
if (process.env.ANTHROPIC_API_KEY) {
|
|
38
39
|
return "anthropic";
|
|
39
40
|
}
|
|
@@ -47,21 +48,33 @@ var Compressor = class {
|
|
|
47
48
|
}
|
|
48
49
|
buildPrompt(messages, existingObservations) {
|
|
49
50
|
return [
|
|
50
|
-
"You are an observer that compresses raw AI session messages into durable observations.",
|
|
51
|
+
"You are an observer that compresses raw AI session messages into durable, human-meaningful observations.",
|
|
51
52
|
"",
|
|
52
53
|
"Rules:",
|
|
53
54
|
"- Output markdown only.",
|
|
54
55
|
"- Group observations by date heading: ## YYYY-MM-DD",
|
|
55
56
|
"- Each line must follow: <emoji> <HH:MM> <observation>",
|
|
56
57
|
"- Priority emojis: \u{1F534} critical, \u{1F7E1} notable, \u{1F7E2} info",
|
|
57
|
-
"- \u{1F534} for: decisions between alternatives,
|
|
58
|
-
"- \u{1F7E1} for: preferences, architecture discussions, trade-offs, milestones, people interactions, notable context
|
|
59
|
-
"- \u{1F7E2} for:
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"-
|
|
63
|
-
"
|
|
64
|
-
|
|
58
|
+
"- \u{1F534} for: decisions between alternatives, blockers, deadlines with explicit dates, breaking changes, commitments made to people",
|
|
59
|
+
"- \u{1F7E1} for: preferences, architecture discussions, trade-offs, milestones, people interactions, notable context",
|
|
60
|
+
"- \u{1F7E2} for: completed tasks, deployments, builds, general progress",
|
|
61
|
+
"",
|
|
62
|
+
"QUALITY FILTERS (important):",
|
|
63
|
+
"- DO NOT observe: CLI errors, command failures, tool output parsing issues, retry attempts, debug logs.",
|
|
64
|
+
" These are transient noise, not memories. Only observe errors if they represent a BLOCKER or an unresolved problem.",
|
|
65
|
+
'- DO NOT observe: "acknowledged the conversation", "said okay", routine confirmations.',
|
|
66
|
+
'- MERGE related events into single observations. If 5 images were generated, say "Generated 5 images for X" not 5 separate lines.',
|
|
67
|
+
'- MERGE retry sequences: "Tried X, failed, tried Y, succeeded" \u2192 "Resolved X using Y (after initial failure)"',
|
|
68
|
+
'- Prefer OUTCOMES over PROCESSES: "Deployed v1.2 to Railway" not "Started deploy... build finished... deploy succeeded"',
|
|
69
|
+
"",
|
|
70
|
+
"AGENT ATTRIBUTION:",
|
|
71
|
+
'- If the transcript shows multiple speakers/agents, prefix observations with who did it: "Pedro asked...", "Clawdious deployed...", "Zeca generated..."',
|
|
72
|
+
"- If only one agent is acting, attribution is optional.",
|
|
73
|
+
"",
|
|
74
|
+
"COMMITMENT FORMAT (when someone promises/agrees to something):",
|
|
75
|
+
'- Use: "\u{1F534} HH:MM [COMMITMENT] <who> committed to <what> by <when>" (include deadline if mentioned)',
|
|
76
|
+
"",
|
|
77
|
+
"Keep observations concise and factual. Aim for signal, not completeness.",
|
|
65
78
|
"",
|
|
66
79
|
"Existing observations (may be empty):",
|
|
67
80
|
existingObservations.trim() || "(none)",
|
|
@@ -537,10 +550,57 @@ var Router = class {
|
|
|
537
550
|
normalizeForDedup(content) {
|
|
538
551
|
return content.replace(/^\d{2}:\d{2}\s+/, "").replace(/\[\[[^\]]*\]\]/g, (m) => m.replace(/\[\[|\]\]/g, "")).replace(/\s+/g, " ").trim().toLowerCase();
|
|
539
552
|
}
|
|
540
|
-
|
|
553
|
+
/**
|
|
554
|
+
* Extract entity slug from observation content for people/projects routing.
|
|
555
|
+
* Returns null if no entity can be identified.
|
|
556
|
+
*/
|
|
557
|
+
extractEntitySlug(content, category) {
|
|
558
|
+
if (category !== "people" && category !== "projects") return null;
|
|
559
|
+
if (category === "people") {
|
|
560
|
+
const patterns = [
|
|
561
|
+
/(?:talked to|met with|spoke with|chatted with|discussed with|emailed|called|messaged)\s+([A-Z][a-z]+(?:\s+[A-Z][a-z]+)?)/,
|
|
562
|
+
/([A-Z][a-z]+(?:\s+[A-Z][a-z]+)?)\s+(?:said|asked|told|mentioned|from|at)\b/,
|
|
563
|
+
/\b(?:client|partner|colleague|contact)\s+([A-Z][a-z]+(?:\s+[A-Z][a-z]+)?)/i
|
|
564
|
+
];
|
|
565
|
+
for (const pattern of patterns) {
|
|
566
|
+
const match = content.match(pattern);
|
|
567
|
+
if (match?.[1]) return this.toSlug(match[1]);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (category === "projects") {
|
|
571
|
+
const patterns = [
|
|
572
|
+
/(?:deployed|shipped|launched|released|built|created|working on)\s+([A-Z][a-zA-Z0-9-]+)/,
|
|
573
|
+
/"([^"]+)"\s+(?:project|repo|service)/i
|
|
574
|
+
];
|
|
575
|
+
for (const pattern of patterns) {
|
|
576
|
+
const match = content.match(pattern);
|
|
577
|
+
if (match?.[1]) return this.toSlug(match[1]);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return null;
|
|
581
|
+
}
|
|
582
|
+
toSlug(name) {
|
|
583
|
+
return name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Resolve the file path for a routed item.
|
|
587
|
+
* For people/projects: entity-slug subfolder with date file (e.g., people/pedro/2026-02-12.md)
|
|
588
|
+
* For other categories: category/date.md
|
|
589
|
+
*/
|
|
590
|
+
resolveFilePath(category, item) {
|
|
591
|
+
const entitySlug = this.extractEntitySlug(item.content, category);
|
|
592
|
+
if (entitySlug) {
|
|
593
|
+
const entityDir = path.join(this.vaultPath, category, entitySlug);
|
|
594
|
+
fs.mkdirSync(entityDir, { recursive: true });
|
|
595
|
+
return path.join(entityDir, `${item.date}.md`);
|
|
596
|
+
}
|
|
541
597
|
const categoryDir = path.join(this.vaultPath, category);
|
|
542
598
|
fs.mkdirSync(categoryDir, { recursive: true });
|
|
543
|
-
|
|
599
|
+
return path.join(categoryDir, `${item.date}.md`);
|
|
600
|
+
}
|
|
601
|
+
appendToCategory(category, item) {
|
|
602
|
+
const filePath = this.resolveFilePath(category, item);
|
|
603
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
544
604
|
const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf-8").trim() : "";
|
|
545
605
|
const normalizedNew = this.normalizeForDedup(item.content);
|
|
546
606
|
const existingLines = existing.split(/\r?\n/);
|
|
@@ -559,7 +619,9 @@ var Router = class {
|
|
|
559
619
|
}
|
|
560
620
|
const linkedContent = this.addWikiLinks(item.content);
|
|
561
621
|
const entry = `- ${item.priority} ${linkedContent}`;
|
|
562
|
-
const
|
|
622
|
+
const entitySlug = this.extractEntitySlug(item.content, category);
|
|
623
|
+
const headerLabel = entitySlug ? `${category}/${entitySlug}` : category;
|
|
624
|
+
const header = existing ? "" : `# ${headerLabel} \u2014 ${item.date}
|
|
563
625
|
`;
|
|
564
626
|
const newContent = existing ? `${existing}
|
|
565
627
|
${entry}
|
package/dist/commands/observe.js
CHANGED
package/dist/commands/sleep.js
CHANGED
package/dist/commands/wake.d.ts
CHANGED
package/dist/commands/wake.js
CHANGED
|
@@ -43,22 +43,35 @@ function formatDateKey(date) {
|
|
|
43
43
|
}
|
|
44
44
|
function readRecentObservationHighlights(vaultPath) {
|
|
45
45
|
const now = /* @__PURE__ */ new Date();
|
|
46
|
-
const yesterday = new Date(now);
|
|
47
|
-
yesterday.setDate(now.getDate() - 1);
|
|
48
|
-
const dateKeys = [formatDateKey(now), formatDateKey(yesterday)];
|
|
49
46
|
const highlights = [];
|
|
50
|
-
for (
|
|
47
|
+
for (let daysAgo = 0; daysAgo < 7; daysAgo++) {
|
|
48
|
+
const date = new Date(now);
|
|
49
|
+
date.setDate(now.getDate() - daysAgo);
|
|
50
|
+
const dateKey = formatDateKey(date);
|
|
51
51
|
const filePath = path.join(vaultPath, "observations", `${dateKey}.md`);
|
|
52
52
|
if (!fs.existsSync(filePath)) continue;
|
|
53
53
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
54
|
+
const dayRed = [];
|
|
55
|
+
const dayYellow = [];
|
|
54
56
|
for (const line of content.split(/\r?\n/)) {
|
|
55
57
|
const match = line.trim().match(OBSERVATION_HIGHLIGHT_RE);
|
|
56
58
|
if (!match?.[2]) continue;
|
|
57
|
-
|
|
59
|
+
const item = {
|
|
58
60
|
date: dateKey,
|
|
59
61
|
priority: match[1],
|
|
60
62
|
text: match[2].trim()
|
|
61
|
-
}
|
|
63
|
+
};
|
|
64
|
+
if (item.priority === "\u{1F534}") dayRed.push(item);
|
|
65
|
+
else dayYellow.push(item);
|
|
66
|
+
}
|
|
67
|
+
if (daysAgo === 0) {
|
|
68
|
+
highlights.push(...dayRed, ...dayYellow);
|
|
69
|
+
} else if (daysAgo === 1) {
|
|
70
|
+
highlights.push(...dayRed, ...dayYellow.slice(0, 5));
|
|
71
|
+
} else if (daysAgo <= 3) {
|
|
72
|
+
highlights.push(...dayRed);
|
|
73
|
+
} else {
|
|
74
|
+
highlights.push(...dayRed.slice(0, 3));
|
|
62
75
|
}
|
|
63
76
|
}
|
|
64
77
|
return highlights;
|
|
@@ -113,6 +126,56 @@ function formatRecentObservations(highlights) {
|
|
|
113
126
|
}
|
|
114
127
|
return lines.join("\n").trim();
|
|
115
128
|
}
|
|
129
|
+
async function generateExecutiveSummary(recovery, recap, highlights) {
|
|
130
|
+
if (process.env.CLAWVAULT_NO_LLM || process.env.VITEST) return null;
|
|
131
|
+
const apiKey = process.env.GEMINI_API_KEY || process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY;
|
|
132
|
+
if (!apiKey) return null;
|
|
133
|
+
const redItems = highlights.filter((h) => h.priority === "\u{1F534}").map((h) => h.text).slice(0, 10);
|
|
134
|
+
const yellowItems = highlights.filter((h) => h.priority === "\u{1F7E1}").map((h) => h.text).slice(0, 5);
|
|
135
|
+
const projects = recap.activeProjects.slice(0, 8);
|
|
136
|
+
const commitments = recap.pendingCommitments.slice(0, 5);
|
|
137
|
+
const lastWork = recovery.checkpoint?.workingOn || recap.recentHandoffs[0]?.workingOn?.join(", ") || "";
|
|
138
|
+
const blockers = recovery.checkpoint?.blocked || recap.recentHandoffs[0]?.blocked?.join(", ") || "";
|
|
139
|
+
const nextSteps = recap.recentHandoffs[0]?.nextSteps?.join(", ") || "";
|
|
140
|
+
const prompt = [
|
|
141
|
+
"You are a chief of staff briefing an AI agent waking up for a new session.",
|
|
142
|
+
"Write a 3-5 sentence executive summary answering: What matters RIGHT NOW?",
|
|
143
|
+
"Be direct and specific. No headers, no bullets \u2014 just a tight paragraph.",
|
|
144
|
+
"Mention the most urgent item first. Include deadlines if any.",
|
|
145
|
+
"",
|
|
146
|
+
`Last working on: ${lastWork || "(unknown)"}`,
|
|
147
|
+
`Blockers: ${blockers || "(none)"}`,
|
|
148
|
+
`Next steps: ${nextSteps || "(none)"}`,
|
|
149
|
+
`Active projects (${projects.length}): ${projects.join(", ") || "(none)"}`,
|
|
150
|
+
`Pending commitments: ${commitments.join(", ") || "(none)"}`,
|
|
151
|
+
`Critical observations: ${redItems.join(" | ") || "(none)"}`,
|
|
152
|
+
`Notable observations: ${yellowItems.join(" | ") || "(none)"}`,
|
|
153
|
+
"",
|
|
154
|
+
"Write the briefing now. Be concise."
|
|
155
|
+
].join("\n");
|
|
156
|
+
try {
|
|
157
|
+
if (process.env.GEMINI_API_KEY) {
|
|
158
|
+
const model = "gemini-2.0-flash";
|
|
159
|
+
const resp = await fetch(
|
|
160
|
+
`https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${process.env.GEMINI_API_KEY}`,
|
|
161
|
+
{
|
|
162
|
+
method: "POST",
|
|
163
|
+
headers: { "content-type": "application/json" },
|
|
164
|
+
body: JSON.stringify({
|
|
165
|
+
contents: [{ parts: [{ text: prompt }] }],
|
|
166
|
+
generationConfig: { temperature: 0.3, maxOutputTokens: 300 }
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
if (!resp.ok) return null;
|
|
171
|
+
const data = await resp.json();
|
|
172
|
+
return data.candidates?.[0]?.content?.parts?.[0]?.text?.trim() || null;
|
|
173
|
+
}
|
|
174
|
+
} catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
116
179
|
async function wake(options) {
|
|
117
180
|
const vaultPath = path.resolve(options.vaultPath);
|
|
118
181
|
const recovery = await recover(vaultPath, { clearFlag: true });
|
|
@@ -125,12 +188,19 @@ async function wake(options) {
|
|
|
125
188
|
});
|
|
126
189
|
const highlights = readRecentObservationHighlights(vaultPath);
|
|
127
190
|
const observations = formatRecentObservations(highlights);
|
|
191
|
+
const execSummary = options.noSummary ? null : await generateExecutiveSummary(recovery, recap, highlights);
|
|
128
192
|
const highlightSummaryItems = highlights.map((item) => `${item.priority} ${item.text}`);
|
|
129
193
|
const wakeSummary = formatSummaryItems(highlightSummaryItems);
|
|
130
194
|
const baseSummary = buildWakeSummary(recovery, recap);
|
|
131
|
-
const
|
|
195
|
+
const fullBaseSummary = wakeSummary ? `${baseSummary} | ${wakeSummary}` : baseSummary;
|
|
196
|
+
const summary = execSummary || fullBaseSummary;
|
|
132
197
|
const baseRecapMarkdown = vault.formatRecap(recap, { brief: options.brief ?? true }).trimEnd();
|
|
133
|
-
const
|
|
198
|
+
const execSection = execSummary ? `## \u{1F4CB} Executive Summary
|
|
199
|
+
|
|
200
|
+
${execSummary}
|
|
201
|
+
|
|
202
|
+
` : "";
|
|
203
|
+
const recapMarkdown = `${execSection}${baseRecapMarkdown}
|
|
134
204
|
|
|
135
205
|
## Recent Observations
|
|
136
206
|
${observations}`;
|
package/dist/index.js
CHANGED
|
@@ -41,13 +41,13 @@ import {
|
|
|
41
41
|
SessionWatcher,
|
|
42
42
|
observeCommand,
|
|
43
43
|
registerObserveCommand
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-GZOBCUVO.js";
|
|
45
45
|
import {
|
|
46
46
|
Compressor,
|
|
47
47
|
Observer,
|
|
48
48
|
Reflector,
|
|
49
49
|
parseSessionFile
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-KOF3HYSL.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.11.
|
|
3
|
+
"version": "1.11.2",
|
|
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",
|