clawvault 3.2.1 → 3.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 (150) hide show
  1. package/README.md +56 -16
  2. package/bin/clawvault.js +0 -2
  3. package/bin/command-registration.test.js +13 -1
  4. package/bin/help-contract.test.js +14 -0
  5. package/bin/register-core-commands.js +88 -0
  6. package/bin/register-core-commands.test.js +80 -0
  7. package/bin/register-maintenance-commands.js +57 -6
  8. package/bin/register-query-commands.js +10 -28
  9. package/bin/test-helpers/cli-command-fixtures.js +1 -0
  10. package/dist/chunk-2PKBIKDH.js +130 -0
  11. package/dist/{chunk-U67V476Y.js → chunk-2ZDO52B4.js} +18 -1
  12. package/dist/{chunk-ZZA73MFY.js → chunk-33DOSHTA.js} +176 -36
  13. package/dist/{chunk-AZYOKJYC.js → chunk-4PY655YM.js} +13 -1
  14. package/dist/{chunk-2JQ3O2YL.js → chunk-5EFSWZO6.js} +3 -3
  15. package/dist/{chunk-Y3TIJEBP.js → chunk-7SWP5FKU.js} +34 -613
  16. package/dist/{chunk-4VQTUVH7.js → chunk-7YZWHM36.js} +52 -26
  17. package/dist/{chunk-URXDAUVH.js → chunk-AXSJIFOJ.js} +174 -1
  18. package/dist/{chunk-4ITRXIVT.js → chunk-BLQXXX7Q.js} +6 -6
  19. package/dist/chunk-CSHO3PJB.js +684 -0
  20. package/dist/{chunk-S5OJEGFG.js → chunk-DOIUYIXV.js} +2 -2
  21. package/dist/{chunk-YXQCA6B7.js → chunk-DVOUSOR3.js} +112 -7
  22. package/dist/{chunk-YDWHS4LJ.js → chunk-ECGJYWNA.js} +205 -33
  23. package/dist/{chunk-QMHPQYUV.js → chunk-EL6UBSX5.js} +7 -6
  24. package/dist/chunk-FZ5I2NF7.js +352 -0
  25. package/dist/{chunk-WJVWINEM.js → chunk-GFCHWMGD.js} +55 -6
  26. package/dist/{chunk-GNJL4YGR.js → chunk-GJO3CFUN.js} +30 -6
  27. package/dist/chunk-H3JZIB5O.js +322 -0
  28. package/dist/chunk-HEHO7SMV.js +51 -0
  29. package/dist/{chunk-UCQAOZHW.js → chunk-HGDDW24U.js} +3 -3
  30. package/dist/chunk-J3YUXVID.js +907 -0
  31. package/dist/{chunk-Y6VJKXGL.js → chunk-KCYWJDDW.js} +1 -1
  32. package/dist/{chunk-P5EPF6MB.js → chunk-MW5C6ZQA.js} +110 -13
  33. package/dist/{chunk-YNIPYN4F.js → chunk-OFOCU2V4.js} +6 -5
  34. package/dist/{chunk-42MXU7A6.js → chunk-P62WHA27.js} +58 -47
  35. package/dist/chunk-PTWPPVC7.js +972 -0
  36. package/dist/{chunk-FAKNOB7Y.js → chunk-QFWERBDP.js} +2 -2
  37. package/dist/{chunk-IIOU45CK.js → chunk-S7N7HI5E.js} +2 -2
  38. package/dist/{chunk-ECRZL5XR.js → chunk-T7E764W3.js} +23 -7
  39. package/dist/chunk-TDWFBDAQ.js +1016 -0
  40. package/dist/{chunk-MNPUYCHQ.js → chunk-TWMI3SNN.js} +6 -5
  41. package/dist/{chunk-2RAZ4ZFE.js → chunk-VBILES4B.js} +1 -1
  42. package/dist/{chunk-PI4WMLMG.js → chunk-VXAGOLDP.js} +1 -1
  43. package/dist/chunk-YCUVAOFC.js +158 -0
  44. package/dist/{chunk-SS4B7P7V.js → chunk-YIDV4VV2.js} +1 -1
  45. package/dist/chunk-ZKWPCBYT.js +600 -0
  46. package/dist/cli/index.js +27 -21
  47. package/dist/commands/archive.js +3 -3
  48. package/dist/commands/backlog.js +1 -1
  49. package/dist/commands/benchmark.d.ts +12 -0
  50. package/dist/commands/benchmark.js +12 -0
  51. package/dist/commands/blocked.js +1 -1
  52. package/dist/commands/canvas.js +2 -2
  53. package/dist/commands/checkpoint.js +1 -1
  54. package/dist/commands/compat.js +1 -1
  55. package/dist/commands/context.js +8 -7
  56. package/dist/commands/doctor.d.ts +8 -3
  57. package/dist/commands/doctor.js +8 -22
  58. package/dist/commands/embed.js +6 -5
  59. package/dist/commands/entities.js +2 -2
  60. package/dist/commands/graph.js +4 -4
  61. package/dist/commands/inbox.d.ts +23 -0
  62. package/dist/commands/inbox.js +11 -0
  63. package/dist/commands/inject.d.ts +1 -1
  64. package/dist/commands/inject.js +5 -5
  65. package/dist/commands/kanban.js +1 -1
  66. package/dist/commands/link.js +9 -9
  67. package/dist/commands/maintain.d.ts +32 -0
  68. package/dist/commands/maintain.js +12 -0
  69. package/dist/commands/migrate-observations.js +3 -3
  70. package/dist/commands/observe.js +11 -10
  71. package/dist/commands/project.js +2 -2
  72. package/dist/commands/rebuild-embeddings.js +48 -17
  73. package/dist/commands/rebuild.js +9 -8
  74. package/dist/commands/recover.js +1 -1
  75. package/dist/commands/reflect.js +6 -6
  76. package/dist/commands/repair-session.js +1 -1
  77. package/dist/commands/replay.js +10 -9
  78. package/dist/commands/session-recap.js +1 -1
  79. package/dist/commands/setup.js +4 -3
  80. package/dist/commands/shell-init.js +1 -1
  81. package/dist/commands/sleep.d.ts +1 -1
  82. package/dist/commands/sleep.js +20 -18
  83. package/dist/commands/status.js +40 -26
  84. package/dist/commands/sync-bd.js +3 -3
  85. package/dist/commands/tailscale.js +3 -3
  86. package/dist/commands/task.js +1 -1
  87. package/dist/commands/template.js +1 -1
  88. package/dist/commands/wake.d.ts +1 -1
  89. package/dist/commands/wake.js +10 -9
  90. package/dist/index.d.ts +175 -16
  91. package/dist/index.js +277 -108
  92. package/dist/{inject-DYUrDqQO.d.ts → inject-DEb_jpLi.d.ts} +3 -1
  93. package/dist/lib/auto-linker.js +2 -2
  94. package/dist/lib/canvas-layout.js +1 -1
  95. package/dist/lib/config.js +2 -2
  96. package/dist/lib/entity-index.js +1 -1
  97. package/dist/lib/project-utils.js +2 -2
  98. package/dist/lib/session-repair.js +1 -1
  99. package/dist/lib/session-utils.js +1 -1
  100. package/dist/lib/tailscale.js +1 -1
  101. package/dist/lib/task-utils.js +1 -1
  102. package/dist/lib/template-engine.js +1 -1
  103. package/dist/lib/webdav.js +1 -1
  104. package/dist/onnxruntime_binding-5QEF3SUC.node +0 -0
  105. package/dist/onnxruntime_binding-BKPKNEGC.node +0 -0
  106. package/dist/onnxruntime_binding-FMOXGIUT.node +0 -0
  107. package/dist/onnxruntime_binding-OI2KMXC5.node +0 -0
  108. package/dist/onnxruntime_binding-UX44MLAZ.node +0 -0
  109. package/dist/onnxruntime_binding-Y2W7N7WY.node +0 -0
  110. package/dist/openclaw-plugin.d.ts +8 -0
  111. package/dist/openclaw-plugin.js +14 -0
  112. package/dist/transformers.node-A2ZRORSQ.js +46775 -0
  113. package/dist/{types-BbWJoC1c.d.ts → types-DslKvCaj.d.ts} +51 -1
  114. package/hooks/clawvault/HOOK.md +25 -8
  115. package/hooks/clawvault/handler.js +215 -78
  116. package/hooks/clawvault/handler.test.js +109 -43
  117. package/hooks/clawvault/integrity.js +112 -0
  118. package/hooks/clawvault/integrity.test.js +32 -0
  119. package/hooks/clawvault/openclaw.plugin.json +133 -15
  120. package/openclaw.plugin.json +131 -203
  121. package/package.json +10 -7
  122. package/bin/register-workgraph-commands.js +0 -451
  123. package/dist/chunk-5PJ4STIC.js +0 -465
  124. package/dist/chunk-ERNE2FZ5.js +0 -189
  125. package/dist/chunk-HR4KN6S2.js +0 -152
  126. package/dist/chunk-IJBFGPCS.js +0 -33
  127. package/dist/chunk-K7PNYS45.js +0 -93
  128. package/dist/chunk-NTOPJI7W.js +0 -207
  129. package/dist/chunk-PG56HX5T.js +0 -154
  130. package/dist/chunk-QPDDIHXE.js +0 -501
  131. package/dist/chunk-WIOLLGAD.js +0 -190
  132. package/dist/chunk-WMGIIABP.js +0 -15
  133. package/dist/ledger-B7g7jhqG.d.ts +0 -44
  134. package/dist/plugin/index.d.ts +0 -352
  135. package/dist/plugin/index.js +0 -4264
  136. package/dist/registry-BR4326o0.d.ts +0 -30
  137. package/dist/store-CA-6sKCJ.d.ts +0 -34
  138. package/dist/thread-B9LhXNU0.d.ts +0 -41
  139. package/dist/workgraph/index.d.ts +0 -5
  140. package/dist/workgraph/index.js +0 -23
  141. package/dist/workgraph/ledger.d.ts +0 -2
  142. package/dist/workgraph/ledger.js +0 -25
  143. package/dist/workgraph/registry.d.ts +0 -2
  144. package/dist/workgraph/registry.js +0 -19
  145. package/dist/workgraph/store.d.ts +0 -2
  146. package/dist/workgraph/store.js +0 -25
  147. package/dist/workgraph/thread.d.ts +0 -2
  148. package/dist/workgraph/thread.js +0 -25
  149. package/dist/workgraph/types.d.ts +0 -54
  150. package/dist/workgraph/types.js +0 -7
@@ -1,29 +1,30 @@
1
+ import {
2
+ requestLlmCompletion,
3
+ resolveLlmProvider
4
+ } from "./chunk-DVOUSOR3.js";
1
5
  import {
2
6
  listProjects
3
- } from "./chunk-AZYOKJYC.js";
7
+ } from "./chunk-4PY655YM.js";
8
+ import {
9
+ listConfig,
10
+ listRouteRules,
11
+ matchRouteRule
12
+ } from "./chunk-AXSJIFOJ.js";
4
13
  import {
5
14
  FactStore,
6
15
  extractFactsLlm,
7
16
  extractFactsRuleBased
8
17
  } from "./chunk-BSJ6RIT7.js";
9
18
  import {
10
- DATE_HEADING_RE,
11
- inferObservationType,
19
+ Compressor
20
+ } from "./chunk-J3YUXVID.js";
21
+ import {
12
22
  normalizeObservationContent,
13
23
  parseObservationLine,
14
24
  parseObservationMarkdown,
15
25
  renderObservationMarkdown,
16
26
  renderScoredObservationLine
17
27
  } from "./chunk-FHFUXL6G.js";
18
- import {
19
- listConfig,
20
- listRouteRules,
21
- matchRouteRule
22
- } from "./chunk-URXDAUVH.js";
23
- import {
24
- requestLlmCompletion,
25
- resolveLlmProvider
26
- } from "./chunk-YXQCA6B7.js";
27
28
  import {
28
29
  ensureLedgerStructure,
29
30
  ensureParentDir,
@@ -40,591 +41,8 @@ import {
40
41
  updateTask
41
42
  } from "./chunk-QWQ3TIKS.js";
42
43
 
43
- // src/observer/compressor.ts
44
- var OPENAI_BASE_URL = "https://api.openai.com/v1";
45
- var XAI_BASE_URL = "https://api.x.ai/v1";
46
- var OLLAMA_BASE_URL = "http://localhost:11434/v1";
47
- var DEFAULT_PROVIDER_MODELS = {
48
- anthropic: "claude-3-5-haiku-latest",
49
- openai: "gpt-4o-mini",
50
- gemini: "gemini-2.0-flash",
51
- xai: "grok-2-latest",
52
- "openai-compatible": "gpt-4o-mini",
53
- ollama: "llama3.2",
54
- minimax: "MiniMax-M2.1",
55
- zai: "glm-4.5-air"
56
- };
57
- 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|\bpublish(?:ed)?\b.*@?\d+\.\d+|\bmerge[d]?\s+(?:PR|pull\s+request)\b|\bshipped\b|\breleased?\b.*v?\d+\.\d+|\bsigned\b.*\b(?:contract|agreement|deal)\b|\bpricing\b.*\$|\bdemo\b.*\b(?:completed?|done|finished)\b|\bmeeting\b.*\b(?:completed?|done|finished)\b|\bstrategy\b.*\b(?:pivot|change|shift)\b)/i;
58
- 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;
59
- 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|deploy(?:ed|ment)?|built|configured|launched|proposal|pitch|onboard(?:ed|ing)?|migrat(?:e|ed|ion)|domain|DNS|infra(?:structure)?)\b/i;
60
- var TODO_SIGNAL_RE = /(?:\btodo:\s*|\bwe need to\b|\bdon't forget(?: to)?\b|\bremember to\b|\bmake sure to\b)/i;
61
- var COMMITMENT_TASK_SIGNAL_RE = /\b(?:i'?ll|i will|let me|(?:i'?m\s+)?going to|plan to|should)\b/i;
62
- var UNRESOLVED_COMMITMENT_RE = /\b(?:need to figure out|tbd|to be determined)\b/i;
63
- var DEADLINE_SIGNAL_RE = /\b(?:by\s+(?:monday|tuesday|wednesday|thursday|friday|saturday|sunday|tomorrow)|before\s+the\s+\w+|deadline is)\b/i;
64
- var Compressor = class {
65
- provider;
66
- model;
67
- baseUrl;
68
- apiKey;
69
- now;
70
- fetchImpl;
71
- constructor(options = {}) {
72
- this.provider = options.provider;
73
- this.model = options.model;
74
- this.baseUrl = options.baseUrl;
75
- this.apiKey = options.apiKey;
76
- this.now = options.now ?? (() => /* @__PURE__ */ new Date());
77
- this.fetchImpl = options.fetchImpl ?? fetch;
78
- }
79
- async compress(messages, existingObservations) {
80
- const cleanedMessages = messages.map((message) => message.trim()).filter(Boolean);
81
- if (cleanedMessages.length === 0) {
82
- return existingObservations.trim();
83
- }
84
- const prompt = this.buildPrompt(cleanedMessages, existingObservations);
85
- const backend = this.resolveProvider();
86
- if (backend) {
87
- try {
88
- const llmOutput = backend.provider === "anthropic" ? await this.callAnthropic(prompt, backend) : backend.provider === "gemini" ? await this.callGemini(prompt, backend) : backend.provider === "openai" ? await this.callOpenAI(prompt, backend) : backend.provider === "xai" ? await this.callXAI(prompt, backend) : await this.callOpenAICompatible(prompt, backend);
89
- const normalized = this.normalizeLlmOutput(llmOutput);
90
- if (normalized) {
91
- return this.mergeObservations(existingObservations, normalized);
92
- }
93
- } catch {
94
- }
95
- }
96
- const fallback = this.fallbackCompression(cleanedMessages);
97
- return this.mergeObservations(existingObservations, fallback);
98
- }
99
- resolveProvider() {
100
- if (process.env.CLAWVAULT_NO_LLM) return null;
101
- if (this.provider) {
102
- const configured = this.resolveConfiguredProvider(this.provider);
103
- if (configured) {
104
- return configured;
105
- }
106
- return this.resolveProviderFromEnv(false);
107
- }
108
- return this.resolveProviderFromEnv(true);
109
- }
110
- resolveConfiguredProvider(provider) {
111
- const model = this.resolveModel(provider);
112
- if (provider === "anthropic") {
113
- const apiKey2 = this.resolveApiKey(provider);
114
- if (!apiKey2) {
115
- return null;
116
- }
117
- return {
118
- provider,
119
- model,
120
- apiKey: apiKey2
121
- };
122
- }
123
- if (provider === "gemini") {
124
- const apiKey2 = this.resolveApiKey(provider);
125
- if (!apiKey2) {
126
- return null;
127
- }
128
- return {
129
- provider,
130
- model,
131
- apiKey: apiKey2
132
- };
133
- }
134
- if (provider === "openai") {
135
- const apiKey2 = this.resolveApiKey(provider);
136
- if (!apiKey2) {
137
- return null;
138
- }
139
- return {
140
- provider,
141
- model,
142
- apiKey: apiKey2,
143
- baseUrl: this.resolveBaseUrl(provider)
144
- };
145
- }
146
- if (provider === "xai") {
147
- const apiKey2 = this.resolveApiKey(provider);
148
- if (!apiKey2) {
149
- return null;
150
- }
151
- return {
152
- provider,
153
- model,
154
- apiKey: apiKey2,
155
- baseUrl: XAI_BASE_URL
156
- };
157
- }
158
- const apiKey = this.resolveApiKey(provider) ?? void 0;
159
- return {
160
- provider,
161
- model,
162
- apiKey,
163
- baseUrl: this.resolveBaseUrl(provider)
164
- };
165
- }
166
- resolveProviderFromEnv(allowConfiguredModel) {
167
- const anthropicApiKey = this.readEnvValue("ANTHROPIC_API_KEY");
168
- if (anthropicApiKey) {
169
- return {
170
- provider: "anthropic",
171
- model: allowConfiguredModel ? this.resolveModel("anthropic") : DEFAULT_PROVIDER_MODELS.anthropic,
172
- apiKey: anthropicApiKey
173
- };
174
- }
175
- const openAiApiKey = this.readEnvValue("OPENAI_API_KEY");
176
- if (openAiApiKey) {
177
- return {
178
- provider: "openai",
179
- model: allowConfiguredModel ? this.resolveModel("openai") : DEFAULT_PROVIDER_MODELS.openai,
180
- apiKey: openAiApiKey,
181
- baseUrl: OPENAI_BASE_URL
182
- };
183
- }
184
- const geminiApiKey = this.readEnvValue("GEMINI_API_KEY");
185
- if (geminiApiKey) {
186
- return {
187
- provider: "gemini",
188
- model: allowConfiguredModel ? this.resolveModel("gemini") : DEFAULT_PROVIDER_MODELS.gemini,
189
- apiKey: geminiApiKey
190
- };
191
- }
192
- const xaiApiKey = this.readEnvValue("XAI_API_KEY");
193
- if (xaiApiKey) {
194
- return {
195
- provider: "xai",
196
- model: allowConfiguredModel ? this.resolveModel("xai") : DEFAULT_PROVIDER_MODELS.xai,
197
- apiKey: xaiApiKey,
198
- baseUrl: XAI_BASE_URL
199
- };
200
- }
201
- return null;
202
- }
203
- resolveModel(provider) {
204
- const configuredModel = this.model?.trim();
205
- if (configuredModel) {
206
- return configuredModel;
207
- }
208
- return DEFAULT_PROVIDER_MODELS[provider];
209
- }
210
- resolveApiKey(provider) {
211
- const configuredApiKey = this.apiKey?.trim();
212
- if (configuredApiKey) {
213
- return configuredApiKey;
214
- }
215
- if (provider === "anthropic") {
216
- return this.readEnvValue("ANTHROPIC_API_KEY");
217
- }
218
- if (provider === "gemini") {
219
- return this.readEnvValue("GEMINI_API_KEY");
220
- }
221
- if (provider === "xai") {
222
- return this.readEnvValue("XAI_API_KEY");
223
- }
224
- return this.readEnvValue("OPENAI_API_KEY");
225
- }
226
- resolveBaseUrl(provider) {
227
- const configuredBaseUrl = this.baseUrl?.trim();
228
- if (configuredBaseUrl) {
229
- return configuredBaseUrl.replace(/\/+$/, "");
230
- }
231
- if (provider === "ollama") {
232
- return OLLAMA_BASE_URL;
233
- }
234
- return OPENAI_BASE_URL;
235
- }
236
- readEnvValue(name) {
237
- const value = process.env[name]?.trim();
238
- return value ? value : null;
239
- }
240
- buildPrompt(messages, existingObservations) {
241
- return [
242
- "You are an observer that compresses raw AI session messages into durable, human-meaningful observations.",
243
- "",
244
- "Rules:",
245
- "- Output markdown only.",
246
- "- Group observations by date heading: ## YYYY-MM-DD",
247
- "- Each observation line MUST follow: - [type|c=<0.00-1.00>|i=<0.00-1.00>] <observation>",
248
- "- Allowed type tags: decision, preference, fact, commitment, task, todo, commitment-unresolved, milestone, lesson, relationship, project",
249
- "- i >= 0.80 for structural/persistent observations (major decisions, blockers, releases, commitments)",
250
- "- i 0.40-0.79 for potentially important observations (notable context, preferences, milestones)",
251
- "- i < 0.40 for contextual/routine observations",
252
- "- Confidence c reflects extraction certainty, not importance.",
253
- "- Preserve source tags when present (e.g., [main], [telegram-dm], [discord], [telegram-group]).",
254
- "",
255
- "TASK EXTRACTION (required):",
256
- `- Emit [todo] for explicit TODO phrasing: "TODO:", "we need to", "don't forget", "remember to", "make sure to".`,
257
- `- Emit [task] for commitments/action intent: "I'll", "I will", "let me", "going to", "plan to", "should".`,
258
- '- Emit [commitment-unresolved] for unresolved commitments/questions: "need to figure out", "TBD", "to be determined".',
259
- '- Deadline language ("by Friday", "before the demo", "deadline is") should increase importance and usually map to [task] unless unresolved.',
260
- "",
261
- "QUALITY FILTERS (important):",
262
- "- DO NOT observe: CLI errors, command failures, tool output parsing issues, retry attempts, debug logs.",
263
- " These are transient noise, not memories. Only observe errors if they represent a BLOCKER or an unresolved problem.",
264
- '- DO NOT observe: "acknowledged the conversation", "said okay", routine confirmations.',
265
- '- MERGE related events into single observations. If 5 images were generated, say "Generated 5 images for X" not 5 separate lines.',
266
- '- MERGE retry sequences: "Tried X, failed, tried Y, succeeded" \u2192 "Resolved X using Y (after initial failure)"',
267
- '- Prefer OUTCOMES over PROCESSES: "Deployed v1.2 to Railway" not "Started deploy... build finished... deploy succeeded"',
268
- "",
269
- "AGENT ATTRIBUTION:",
270
- '- If the transcript shows multiple speakers/agents, prefix observations with who did it: "Pedro asked...", "Clawdious deployed...", "Zeca generated..."',
271
- "- If only one agent is acting, attribution is optional.",
272
- "",
273
- "PROJECT MILESTONES (critical \u2014 these are the most valuable observations):",
274
- "Projects are NOT just code. Milestones include business, strategy, client, and operational events.",
275
- "- Use milestone/decision/commitment types for strategic events with high importance.",
276
- "- Use preference/lesson/relationship/project/fact when appropriate.",
277
- "- Examples:",
278
- ' "- [decision|c=0.95|i=0.90] 14:00 Pricing decision: $33K one-time + $3K/mo for Artemisa"',
279
- ' "- [milestone|c=0.93|i=0.88] 14:00 Published clawvault@2.1.0 to npm"',
280
- ' "- [project|c=0.84|i=0.58] 14:00 Deployed pitch deck to artemisa-pitch-deck.vercel.app"',
281
- "- Do NOT collapse multiple milestones into one line \u2014 each matters for history.",
282
- "",
283
- "COMMITMENT FORMAT (when someone promises/agrees to something):",
284
- '- Prefer: "- [commitment|c=...|i=...] HH:MM [COMMITMENT] <who> committed to <what> by <when>"',
285
- "",
286
- "Keep observations concise and factual. Aim for signal, not completeness.",
287
- "",
288
- "Existing observations (may be empty):",
289
- existingObservations.trim() || "(none)",
290
- "",
291
- "Raw messages:",
292
- ...messages.map((message, index) => `[${index + 1}] ${message}`),
293
- "",
294
- "Return only the updated observation markdown."
295
- ].join("\n");
296
- }
297
- buildOpenAICompatibleUrl(baseUrl) {
298
- const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
299
- return `${normalizedBaseUrl}/chat/completions`;
300
- }
301
- buildOpenAICompatibleHeaders(apiKey) {
302
- const headers = {
303
- "content-type": "application/json"
304
- };
305
- if (apiKey) {
306
- headers.authorization = `Bearer ${apiKey}`;
307
- }
308
- return headers;
309
- }
310
- extractOpenAIContent(content) {
311
- if (typeof content === "string") {
312
- return content.trim();
313
- }
314
- if (!Array.isArray(content)) {
315
- return "";
316
- }
317
- const parts = content.map((part) => {
318
- if (typeof part === "string") {
319
- return part;
320
- }
321
- if (!part || typeof part !== "object") {
322
- return "";
323
- }
324
- const candidate = part;
325
- return typeof candidate.text === "string" ? candidate.text : "";
326
- }).filter((part) => part.trim().length > 0);
327
- return parts.join("\n").trim();
328
- }
329
- async callAnthropic(prompt, backend) {
330
- if (!backend.apiKey) {
331
- return "";
332
- }
333
- const response = await this.fetchImpl("https://api.anthropic.com/v1/messages", {
334
- method: "POST",
335
- headers: {
336
- "content-type": "application/json",
337
- "x-api-key": backend.apiKey,
338
- "anthropic-version": "2023-06-01"
339
- },
340
- body: JSON.stringify({
341
- model: backend.model,
342
- temperature: 0.1,
343
- max_tokens: 1400,
344
- messages: [{ role: "user", content: prompt }]
345
- })
346
- });
347
- if (!response.ok) {
348
- throw new Error(`Anthropic request failed (${response.status})`);
349
- }
350
- const payload = await response.json();
351
- return payload.content?.filter((part) => part.type === "text" && part.text).map((part) => part.text).join("\n").trim() ?? "";
352
- }
353
- async callOpenAI(prompt, backend) {
354
- return this.callOpenAICompatible(prompt, backend);
355
- }
356
- async callXAI(prompt, backend) {
357
- return this.callOpenAICompatible(prompt, backend);
358
- }
359
- async callOpenAICompatible(prompt, backend) {
360
- const baseUrl = backend.baseUrl ?? this.resolveBaseUrl(backend.provider);
361
- const response = await this.fetchImpl(this.buildOpenAICompatibleUrl(baseUrl), {
362
- method: "POST",
363
- headers: this.buildOpenAICompatibleHeaders(backend.apiKey),
364
- body: JSON.stringify({
365
- model: backend.model,
366
- temperature: 0.1,
367
- messages: [
368
- { role: "system", content: "You transform session logs into concise observations." },
369
- { role: "user", content: prompt }
370
- ]
371
- })
372
- });
373
- if (!response.ok) {
374
- throw new Error(`OpenAI-compatible request failed (${response.status})`);
375
- }
376
- const payload = await response.json();
377
- return this.extractOpenAIContent(payload.choices?.[0]?.message?.content);
378
- }
379
- async callGemini(prompt, backend) {
380
- if (!backend.apiKey) {
381
- return "";
382
- }
383
- const model = encodeURIComponent(backend.model);
384
- const response = await this.fetchImpl(
385
- `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${backend.apiKey}`,
386
- {
387
- method: "POST",
388
- headers: { "content-type": "application/json" },
389
- body: JSON.stringify({
390
- contents: [{ parts: [{ text: prompt }] }],
391
- generationConfig: { temperature: 0.1, maxOutputTokens: 1400 }
392
- })
393
- }
394
- );
395
- if (!response.ok) {
396
- throw new Error(`Gemini request failed (${response.status})`);
397
- }
398
- const payload = await response.json();
399
- return payload.candidates?.[0]?.content?.parts?.[0]?.text?.trim() ?? "";
400
- }
401
- normalizeLlmOutput(output) {
402
- if (!output.trim()) {
403
- return "";
404
- }
405
- const cleaned = output.replace(/^```(?:markdown)?\s*/i, "").replace(/\s*```$/, "").trim();
406
- const lines = cleaned.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
407
- const hasObservationLine = lines.some((line) => line.startsWith("- [") || /^(?:-\s*)?(🔴|🟡|🟢)\s+/.test(line));
408
- if (!hasObservationLine) {
409
- return "";
410
- }
411
- const hasDateHeading = lines.some((line) => DATE_HEADING_RE.test(line));
412
- const result = hasDateHeading ? cleaned : `## ${this.formatDate(this.now())}
413
-
414
- ${cleaned}`;
415
- const sanitized = this.sanitizeWikiLinks(result);
416
- return this.enforceImportanceRules(sanitized);
417
- }
418
- /**
419
- * Fix wiki-link corruption from LLM compression.
420
- * LLMs often fuse preceding word fragments into wiki-links during rewriting:
421
- * "reque[[people/pedro]]" → "[[people/pedro]]"
422
- * "Linke[[agents/zeca]]" → "[[agents/zeca]]"
423
- * "taske[[people/pedro]]a" → "[[people/pedro]]"
424
- * Also fixes trailing word fragments fused after closing brackets.
425
- */
426
- sanitizeWikiLinks(markdown) {
427
- let result = markdown.replace(/\w+\[\[/g, " [[");
428
- result = result.replace(/\]\]\w+/g, "]]");
429
- result = result.replace(/ {2,}/g, " ");
430
- return result;
431
- }
432
- enforceImportanceRules(markdown) {
433
- const parsed = parseObservationMarkdown(markdown);
434
- if (parsed.length === 0) {
435
- return "";
436
- }
437
- const grouped = /* @__PURE__ */ new Map();
438
- for (const record of parsed) {
439
- const adjusted = this.enforceImportanceForRecord(record);
440
- const bucket = grouped.get(record.date) ?? [];
441
- bucket.push(adjusted);
442
- grouped.set(record.date, bucket);
443
- }
444
- return renderObservationMarkdown(grouped);
445
- }
446
- enforceImportanceForRecord(record) {
447
- let importance = record.importance;
448
- let confidence = record.confidence;
449
- let type = record.type;
450
- const inferredTaskType = this.inferTaskType(record.content);
451
- if (this.isCriticalContent(record.content)) {
452
- importance = Math.max(importance, 0.85);
453
- confidence = Math.max(confidence, 0.85);
454
- if (type === "fact") {
455
- type = inferObservationType(record.content);
456
- }
457
- } else if (this.isNotableContent(record.content)) {
458
- importance = Math.max(importance, 0.5);
459
- confidence = Math.max(confidence, 0.75);
460
- }
461
- if (inferredTaskType) {
462
- type = type === "fact" || type === "commitment" ? inferredTaskType : type;
463
- importance = Math.max(importance, inferredTaskType === "commitment-unresolved" ? 0.72 : 0.65);
464
- confidence = Math.max(confidence, 0.8);
465
- }
466
- if (type === "decision" || type === "commitment" || type === "milestone") {
467
- importance = Math.max(importance, 0.6);
468
- }
469
- return {
470
- type,
471
- confidence: this.clamp01(confidence),
472
- importance: this.clamp01(importance),
473
- content: record.content
474
- };
475
- }
476
- fallbackCompression(messages) {
477
- const sections = /* @__PURE__ */ new Map();
478
- const seen = /* @__PURE__ */ new Set();
479
- for (const message of messages) {
480
- const normalized = this.normalizeText(message);
481
- if (!normalized) continue;
482
- const date = this.extractDate(message) ?? this.formatDate(this.now());
483
- const time = this.extractTime(message) ?? this.formatTime(this.now());
484
- const line = `${time} ${normalized}`;
485
- const type = inferObservationType(line);
486
- const importance = this.inferImportance(line, type);
487
- const confidence = this.inferConfidence(line, type, importance);
488
- const dedupeKey = `${date}|${type}|${normalizeObservationContent(line)}`;
489
- if (seen.has(dedupeKey)) continue;
490
- seen.add(dedupeKey);
491
- const bucket = sections.get(date) ?? [];
492
- bucket.push({ type, confidence, importance, content: line });
493
- sections.set(date, bucket);
494
- }
495
- if (sections.size === 0) {
496
- const date = this.formatDate(this.now());
497
- sections.set(date, [{
498
- type: "fact",
499
- confidence: 0.7,
500
- importance: 0.2,
501
- content: `${this.formatTime(this.now())} Processed session updates.`
502
- }]);
503
- }
504
- return this.renderSections(sections);
505
- }
506
- mergeObservations(existing, incoming) {
507
- const existingRecords = parseObservationMarkdown(existing);
508
- const incomingRecords = parseObservationMarkdown(incoming);
509
- if (incomingRecords.length === 0) {
510
- return existing.trim();
511
- }
512
- const merged = /* @__PURE__ */ new Map();
513
- for (const record of existingRecords) {
514
- this.mergeRecord(merged, {
515
- date: record.date,
516
- type: record.type,
517
- confidence: record.confidence,
518
- importance: record.importance,
519
- content: record.content
520
- });
521
- }
522
- for (const record of incomingRecords) {
523
- this.mergeRecord(merged, {
524
- date: record.date,
525
- type: record.type,
526
- confidence: record.confidence,
527
- importance: record.importance,
528
- content: record.content
529
- });
530
- }
531
- return this.renderSections(merged);
532
- }
533
- mergeRecord(sections, input) {
534
- const bucket = sections.get(input.date) ?? [];
535
- const key = normalizeObservationContent(input.content);
536
- const index = bucket.findIndex((line) => normalizeObservationContent(line.content) === key);
537
- if (index === -1) {
538
- bucket.push({
539
- type: input.type,
540
- confidence: this.clamp01(input.confidence),
541
- importance: this.clamp01(input.importance),
542
- content: input.content.trim()
543
- });
544
- sections.set(input.date, bucket);
545
- return;
546
- }
547
- const existing = bucket[index];
548
- bucket[index] = {
549
- type: input.importance >= existing.importance ? input.type : existing.type,
550
- confidence: this.clamp01(Math.max(existing.confidence, input.confidence)),
551
- importance: this.clamp01(Math.max(existing.importance, input.importance)),
552
- content: existing.content.length >= input.content.length ? existing.content : input.content
553
- };
554
- sections.set(input.date, bucket);
555
- }
556
- renderSections(sections) {
557
- return renderObservationMarkdown(sections);
558
- }
559
- inferImportance(text, type) {
560
- const inferredTaskType = this.inferTaskType(text);
561
- if (this.isCriticalContent(text)) return 0.9;
562
- if (inferredTaskType === "commitment-unresolved") return 0.72;
563
- if (inferredTaskType === "task" || inferredTaskType === "todo") return 0.65;
564
- if (this.isNotableContent(text)) return 0.6;
565
- if (type === "decision" || type === "commitment" || type === "milestone") return 0.55;
566
- if (type === "preference" || type === "lesson" || type === "relationship" || type === "project") return 0.45;
567
- return 0.2;
568
- }
569
- inferConfidence(text, type, importance) {
570
- const inferredTaskType = this.inferTaskType(text);
571
- let confidence = 0.72;
572
- if (importance >= 0.8) confidence += 0.12;
573
- if (type === "decision" || type === "commitment" || type === "milestone") confidence += 0.06;
574
- if (inferredTaskType) confidence += 0.06;
575
- if (/\b(?:decided|chose|committed|deadline|released|merged)\b/i.test(text)) {
576
- confidence += 0.05;
577
- }
578
- return this.clamp01(confidence);
579
- }
580
- isCriticalContent(text) {
581
- return CRITICAL_RE.test(text) || DEADLINE_WITH_DATE_RE.test(text);
582
- }
583
- isNotableContent(text) {
584
- return NOTABLE_RE.test(text);
585
- }
586
- inferTaskType(text) {
587
- if (UNRESOLVED_COMMITMENT_RE.test(text)) {
588
- return "commitment-unresolved";
589
- }
590
- if (TODO_SIGNAL_RE.test(text)) {
591
- return "todo";
592
- }
593
- if (COMMITMENT_TASK_SIGNAL_RE.test(text) || DEADLINE_SIGNAL_RE.test(text)) {
594
- return "task";
595
- }
596
- return null;
597
- }
598
- normalizeText(text) {
599
- return text.replace(/\s+/g, " ").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").trim().slice(0, 280);
600
- }
601
- extractDate(text) {
602
- const match = text.match(/\b(\d{4}-\d{2}-\d{2})\b/);
603
- return match?.[1] ?? null;
604
- }
605
- extractTime(text) {
606
- const match = text.match(/\b([01]\d|2[0-3]):([0-5]\d)\b/);
607
- if (!match) {
608
- return null;
609
- }
610
- return `${match[1]}:${match[2]}`;
611
- }
612
- formatDate(date) {
613
- return date.toISOString().split("T")[0];
614
- }
615
- formatTime(date) {
616
- return date.toISOString().slice(11, 16);
617
- }
618
- clamp01(value) {
619
- if (!Number.isFinite(value)) return 0;
620
- if (value < 0) return 0;
621
- if (value > 1) return 1;
622
- return value;
623
- }
624
- };
625
-
626
44
  // src/observer/reflector.ts
627
- var DATE_HEADING_RE2 = /^##\s+(\d{4}-\d{2}-\d{2})\s*$/;
45
+ var DATE_HEADING_RE = /^##\s+(\d{4}-\d{2}-\d{2})\s*$/;
628
46
  var OBSERVATION_LINE_RE = /^(🔴|🟡|🟢)\s+(.+)$/u;
629
47
  var Reflector = class {
630
48
  now;
@@ -684,7 +102,7 @@ var Reflector = class {
684
102
  const sections = /* @__PURE__ */ new Map();
685
103
  let currentDate = null;
686
104
  for (const rawLine of markdown.split(/\r?\n/)) {
687
- const dateMatch = rawLine.match(DATE_HEADING_RE2);
105
+ const dateMatch = rawLine.match(DATE_HEADING_RE);
688
106
  if (dateMatch) {
689
107
  currentDate = dateMatch[1];
690
108
  if (!sections.has(currentDate)) {
@@ -732,9 +150,8 @@ var Reflector = class {
732
150
  };
733
151
 
734
152
  // src/lib/llm-adapter.ts
735
- var GEMINI_FLASH_MODEL = "gemini-2.0-flash";
736
153
  var OLLAMA_DEFAULT_MODEL = "llama3.1:8b";
737
- var OLLAMA_BASE_URL2 = "http://127.0.0.1:11434";
154
+ var OLLAMA_BASE_URL = "http://127.0.0.1:11434";
738
155
  function createGeminiFlashAdapter(options = {}) {
739
156
  const apiKey = process.env.GEMINI_API_KEY;
740
157
  return {
@@ -745,7 +162,8 @@ function createGeminiFlashAdapter(options = {}) {
745
162
  return requestLlmCompletion({
746
163
  prompt,
747
164
  provider: "gemini",
748
- model: options.model ?? GEMINI_FLASH_MODEL,
165
+ model: options.model,
166
+ tier: options.tier ?? "complex",
749
167
  temperature: options.temperature ?? 0.1,
750
168
  maxTokens: options.maxTokens ?? 2e3,
751
169
  fetchImpl: options.fetchImpl
@@ -764,7 +182,7 @@ function createOllamaAdapter(options = {}) {
764
182
  const fetchFn = options.fetchImpl ?? globalThis.fetch;
765
183
  return {
766
184
  async call(prompt) {
767
- const resp = await fetchFn(`${OLLAMA_BASE_URL2}/api/generate`, {
185
+ const resp = await fetchFn(`${OLLAMA_BASE_URL}/api/generate`, {
768
186
  method: "POST",
769
187
  headers: { "Content-Type": "application/json" },
770
188
  body: JSON.stringify({
@@ -802,6 +220,7 @@ function createDefaultAdapter(options = {}) {
802
220
  prompt,
803
221
  provider: resolvedProvider,
804
222
  model: options.model,
223
+ tier: options.tier ?? "default",
805
224
  temperature: options.temperature ?? 0.1,
806
225
  maxTokens: options.maxTokens ?? 2e3,
807
226
  fetchImpl: options.fetchImpl
@@ -816,18 +235,22 @@ function createDefaultAdapter(options = {}) {
816
235
  };
817
236
  }
818
237
  function createFactExtractionAdapter(options = {}) {
238
+ const factExtractionOptions = {
239
+ ...options,
240
+ tier: options.tier ?? "complex"
241
+ };
819
242
  if (options.provider) {
820
- return createDefaultAdapter(options);
243
+ return createDefaultAdapter(factExtractionOptions);
821
244
  }
822
- const geminiAdapter = createGeminiFlashAdapter(options);
245
+ const geminiAdapter = createGeminiFlashAdapter(factExtractionOptions);
823
246
  if (geminiAdapter.isAvailable()) {
824
247
  return geminiAdapter;
825
248
  }
826
- const ollamaAdapter = createOllamaAdapter(options);
249
+ const ollamaAdapter = createOllamaAdapter(factExtractionOptions);
827
250
  if (ollamaAdapter.isAvailable()) {
828
251
  return ollamaAdapter;
829
252
  }
830
- return createDefaultAdapter(options);
253
+ return createDefaultAdapter(factExtractionOptions);
831
254
  }
832
255
  function createLlmFunction(adapter) {
833
256
  if (!adapter.isAvailable()) {
@@ -1717,14 +1140,13 @@ function readCompressionConfig(vaultPath) {
1717
1140
  const root = asRecord(config);
1718
1141
  const observer = asRecord(root?.observer);
1719
1142
  const compression = asRecord(observer?.compression);
1720
- if (!compression) {
1721
- return {};
1722
- }
1143
+ const models = asRecord(root?.models);
1144
+ const backgroundTierModel = asNonEmptyString(models?.background);
1723
1145
  return {
1724
- provider: asCompressionProvider(compression.provider),
1725
- model: asNonEmptyString(compression.model),
1726
- baseUrl: asNonEmptyString(compression.baseUrl),
1727
- apiKey: asNonEmptyString(compression.apiKey)
1146
+ provider: asCompressionProvider(compression?.provider),
1147
+ model: asNonEmptyString(compression?.model) ?? backgroundTierModel,
1148
+ baseUrl: asNonEmptyString(compression?.baseUrl),
1149
+ apiKey: asNonEmptyString(compression?.apiKey)
1728
1150
  };
1729
1151
  } catch {
1730
1152
  return {};
@@ -1926,7 +1348,6 @@ var Observer = class {
1926
1348
  };
1927
1349
 
1928
1350
  export {
1929
- Compressor,
1930
1351
  Reflector,
1931
1352
  createGeminiFlashAdapter,
1932
1353
  createDefaultAdapter,