clawvault 3.1.0 → 3.2.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 (289) hide show
  1. package/README.md +422 -141
  2. package/bin/clawvault.js +10 -2
  3. package/bin/command-registration.test.js +3 -1
  4. package/bin/command-runtime.js +9 -1
  5. package/bin/register-core-commands.js +23 -28
  6. package/bin/register-maintenance-commands.js +39 -3
  7. package/bin/register-query-commands.js +58 -29
  8. package/bin/register-tailscale-commands.js +106 -0
  9. package/bin/register-task-commands.js +18 -1
  10. package/bin/register-task-commands.test.js +16 -0
  11. package/bin/register-vault-operations-commands.js +29 -1
  12. package/bin/register-workgraph-commands.js +1368 -0
  13. package/dashboard/lib/graph-diff.js +104 -0
  14. package/dashboard/lib/graph-diff.test.js +75 -0
  15. package/dashboard/lib/vault-parser.js +556 -0
  16. package/dashboard/lib/vault-parser.test.js +254 -0
  17. package/dashboard/public/app.js +796 -0
  18. package/dashboard/public/index.html +52 -0
  19. package/dashboard/public/styles.css +221 -0
  20. package/dashboard/server.js +374 -0
  21. package/dist/{chunk-F2JEUD4J.js → chunk-23YDQ3QU.js} +6 -8
  22. package/dist/{chunk-C7OK5WKP.js → chunk-2JQ3O2YL.js} +4 -4
  23. package/dist/{chunk-VR5NE7PZ.js → chunk-2RAZ4ZFE.js} +1 -1
  24. package/dist/chunk-2ZDO52B4.js +52 -0
  25. package/dist/{chunk-ZZA73MFY.js → chunk-33DOSHTA.js} +176 -36
  26. package/dist/chunk-33VSQP4J.js +37 -0
  27. package/dist/chunk-4BQTQMJP.js +93 -0
  28. package/dist/{chunk-GUKMRGM7.js → chunk-4OXMU5S2.js} +1 -1
  29. package/dist/{chunk-62YTUT6J.js → chunk-4PY655YM.js} +15 -3
  30. package/dist/chunk-6FH3IULF.js +352 -0
  31. package/dist/{chunk-3NSBOUT3.js → chunk-77Q5CSPJ.js} +404 -80
  32. package/dist/{chunk-4VQTUVH7.js → chunk-7YZWHM36.js} +52 -26
  33. package/dist/chunk-BSJ6RIT7.js +447 -0
  34. package/dist/chunk-BUEW6IIK.js +364 -0
  35. package/dist/{chunk-LI4O6NVK.js → chunk-CLJTREDS.js} +74 -14
  36. package/dist/chunk-EK6S23ZB.js +469 -0
  37. package/dist/{chunk-LNJA2UGL.js → chunk-ESFLMDRB.js} +9 -86
  38. package/dist/{chunk-H34S76MB.js → chunk-ESVS6K2B.js} +6 -6
  39. package/dist/{chunk-WAZ3NLWL.js → chunk-F55HGNU4.js} +0 -47
  40. package/dist/{chunk-QK3UCXWL.js → chunk-FHFUXL6G.js} +2 -2
  41. package/dist/{chunk-H62BP7RI.js → chunk-GAOWA7GR.js} +212 -46
  42. package/dist/chunk-GGA32J2R.js +784 -0
  43. package/dist/chunk-GNJL4YGR.js +79 -0
  44. package/dist/chunk-IVRIKYFE.js +520 -0
  45. package/dist/chunk-MDIH26GC.js +183 -0
  46. package/dist/{chunk-LYHGEHXG.js → chunk-MFAWT5O5.js} +0 -1
  47. package/dist/chunk-MM6QGW3P.js +207 -0
  48. package/dist/{chunk-P5EPF6MB.js → chunk-MW5C6ZQA.js} +110 -13
  49. package/dist/chunk-NCKFNBHJ.js +257 -0
  50. package/dist/{chunk-QBLMXKF2.js → chunk-OIWVQYQF.js} +1 -1
  51. package/dist/{chunk-42MXU7A6.js → chunk-P62WHA27.js} +58 -47
  52. package/dist/chunk-PBACDKKP.js +66 -0
  53. package/dist/{chunk-VGLOTGAS.js → chunk-QSHD36LH.js} +2 -2
  54. package/dist/{chunk-OZ7RIXTO.js → chunk-QSRRMEYM.js} +2 -2
  55. package/dist/chunk-QVEERJSP.js +152 -0
  56. package/dist/{chunk-N2AXRYLC.js → chunk-QWQ3TIKS.js} +1 -1
  57. package/dist/{chunk-3DHXQHYG.js → chunk-R2MIW5G7.js} +1 -1
  58. package/dist/{chunk-SJSFRIYS.js → chunk-SLXOR3CC.js} +2 -2
  59. package/dist/chunk-SS4B7P7V.js +99 -0
  60. package/dist/{chunk-JY6FYXIT.js → chunk-STCQGCEQ.js} +6 -11
  61. package/dist/chunk-TIGW564L.js +628 -0
  62. package/dist/chunk-U4O6C46S.js +154 -0
  63. package/dist/{chunk-ITPEXLHA.js → chunk-URXDAUVH.js} +24 -5
  64. package/dist/chunk-VSL7KY3M.js +189 -0
  65. package/dist/{chunk-U55BGUAU.js → chunk-W4SPAEE7.js} +6 -6
  66. package/dist/chunk-WMGIIABP.js +15 -0
  67. package/dist/{chunk-33UGEQRT.js → chunk-X3SPPUFG.js} +151 -64
  68. package/dist/chunk-Y6VJKXGL.js +373 -0
  69. package/dist/{chunk-3WRJEKN4.js → chunk-ZN54U2OZ.js} +123 -10
  70. package/dist/cli/index.js +34 -24
  71. package/dist/commands/archive.js +3 -3
  72. package/dist/commands/backlog.js +3 -3
  73. package/dist/commands/blocked.js +3 -3
  74. package/dist/commands/canvas.d.ts +15 -0
  75. package/dist/commands/canvas.js +200 -0
  76. package/dist/commands/checkpoint.js +2 -2
  77. package/dist/commands/compat.js +2 -2
  78. package/dist/commands/context.js +8 -6
  79. package/dist/commands/doctor.d.ts +11 -7
  80. package/dist/commands/doctor.js +18 -16
  81. package/dist/commands/embed.js +5 -6
  82. package/dist/commands/entities.js +2 -2
  83. package/dist/commands/graph.js +4 -4
  84. package/dist/commands/inject.d.ts +1 -1
  85. package/dist/commands/inject.js +5 -6
  86. package/dist/commands/kanban.js +4 -4
  87. package/dist/commands/link.js +5 -5
  88. package/dist/commands/migrate-observations.js +4 -4
  89. package/dist/commands/observe.d.ts +0 -1
  90. package/dist/commands/observe.js +14 -13
  91. package/dist/commands/project.js +5 -5
  92. package/dist/commands/rebuild-embeddings.d.ts +21 -0
  93. package/dist/commands/rebuild-embeddings.js +91 -0
  94. package/dist/commands/rebuild.js +12 -11
  95. package/dist/commands/recover.js +3 -3
  96. package/dist/commands/reflect.js +6 -7
  97. package/dist/commands/repair-session.js +1 -1
  98. package/dist/commands/replay.js +14 -14
  99. package/dist/commands/session-recap.js +1 -1
  100. package/dist/commands/setup.d.ts +2 -89
  101. package/dist/commands/setup.js +3 -21
  102. package/dist/commands/shell-init.js +1 -1
  103. package/dist/commands/sleep.d.ts +1 -1
  104. package/dist/commands/sleep.js +20 -19
  105. package/dist/commands/status.d.ts +2 -0
  106. package/dist/commands/status.js +57 -35
  107. package/dist/commands/sync-bd.d.ts +10 -0
  108. package/dist/commands/sync-bd.js +10 -0
  109. package/dist/commands/tailscale.d.ts +52 -0
  110. package/dist/commands/tailscale.js +26 -0
  111. package/dist/commands/task.js +4 -4
  112. package/dist/commands/template.js +2 -2
  113. package/dist/commands/wake.d.ts +1 -1
  114. package/dist/commands/wake.js +11 -10
  115. package/dist/commands/workgraph.d.ts +124 -0
  116. package/dist/commands/workgraph.js +38 -0
  117. package/dist/index.d.ts +341 -191
  118. package/dist/index.js +446 -116
  119. package/dist/{inject-Bzi5E-By.d.ts → inject-DYUrDqQO.d.ts} +3 -3
  120. package/dist/ledger-B7g7jhqG.d.ts +44 -0
  121. package/dist/lib/auto-linker.js +2 -2
  122. package/dist/lib/canvas-layout.d.ts +115 -0
  123. package/dist/lib/canvas-layout.js +35 -0
  124. package/dist/lib/config.d.ts +27 -3
  125. package/dist/lib/config.js +4 -2
  126. package/dist/lib/entity-index.js +1 -1
  127. package/dist/lib/project-utils.js +4 -4
  128. package/dist/lib/session-repair.js +1 -1
  129. package/dist/lib/session-utils.js +1 -1
  130. package/dist/lib/tailscale.d.ts +225 -0
  131. package/dist/lib/tailscale.js +50 -0
  132. package/dist/lib/task-utils.js +3 -3
  133. package/dist/lib/template-engine.js +1 -1
  134. package/dist/lib/webdav.d.ts +109 -0
  135. package/dist/lib/webdav.js +35 -0
  136. package/dist/onnxruntime_binding-5QEF3SUC.node +0 -0
  137. package/dist/onnxruntime_binding-BKPKNEGC.node +0 -0
  138. package/dist/onnxruntime_binding-FMOXGIUT.node +0 -0
  139. package/dist/onnxruntime_binding-OI2KMXC5.node +0 -0
  140. package/dist/onnxruntime_binding-UX44MLAZ.node +0 -0
  141. package/dist/onnxruntime_binding-Y2W7N7WY.node +0 -0
  142. package/dist/openclaw-plugin.d.ts +8 -0
  143. package/dist/openclaw-plugin.js +14 -0
  144. package/dist/registry-BR4326o0.d.ts +30 -0
  145. package/dist/store-CA-6sKCJ.d.ts +34 -0
  146. package/dist/thread-B9LhXNU0.d.ts +41 -0
  147. package/dist/transformers.node-A2ZRORSQ.js +46775 -0
  148. package/dist/{types-Y2_Um2Ls.d.ts → types-BbWJoC1c.d.ts} +1 -44
  149. package/dist/workgraph/index.d.ts +5 -0
  150. package/dist/workgraph/index.js +23 -0
  151. package/dist/workgraph/ledger.d.ts +2 -0
  152. package/dist/workgraph/ledger.js +25 -0
  153. package/dist/workgraph/registry.d.ts +2 -0
  154. package/dist/workgraph/registry.js +19 -0
  155. package/dist/workgraph/store.d.ts +2 -0
  156. package/dist/workgraph/store.js +25 -0
  157. package/dist/workgraph/thread.d.ts +2 -0
  158. package/dist/workgraph/thread.js +25 -0
  159. package/dist/workgraph/types.d.ts +54 -0
  160. package/dist/workgraph/types.js +7 -0
  161. package/hooks/clawvault/HOOK.md +113 -0
  162. package/hooks/clawvault/handler.js +1561 -0
  163. package/hooks/clawvault/handler.test.js +510 -0
  164. package/hooks/clawvault/openclaw.plugin.json +72 -0
  165. package/openclaw.plugin.json +65 -38
  166. package/package.json +25 -22
  167. package/dist/chunk-3RG5ZIWI.js +0 -10
  168. package/dist/chunk-3ZIH425O.js +0 -871
  169. package/dist/chunk-6U6MK36V.js +0 -205
  170. package/dist/chunk-CMB7UL7C.js +0 -327
  171. package/dist/chunk-D2H45LON.js +0 -1074
  172. package/dist/chunk-E7MFQB6D.js +0 -163
  173. package/dist/chunk-GQSLDZTS.js +0 -560
  174. package/dist/chunk-MFM6K7PU.js +0 -374
  175. package/dist/chunk-MXSSG3QU.js +0 -42
  176. package/dist/chunk-OCGVIN3L.js +0 -88
  177. package/dist/chunk-PAH27GSN.js +0 -108
  178. package/dist/chunk-YCUNCH2I.js +0 -78
  179. package/dist/cli/index.cjs +0 -8584
  180. package/dist/cli/index.d.cts +0 -5
  181. package/dist/commands/archive.cjs +0 -287
  182. package/dist/commands/archive.d.cts +0 -11
  183. package/dist/commands/backlog.cjs +0 -721
  184. package/dist/commands/backlog.d.cts +0 -53
  185. package/dist/commands/blocked.cjs +0 -204
  186. package/dist/commands/blocked.d.cts +0 -26
  187. package/dist/commands/checkpoint.cjs +0 -244
  188. package/dist/commands/checkpoint.d.cts +0 -41
  189. package/dist/commands/compat.cjs +0 -294
  190. package/dist/commands/compat.d.cts +0 -28
  191. package/dist/commands/context.cjs +0 -2990
  192. package/dist/commands/context.d.cts +0 -2
  193. package/dist/commands/doctor.cjs +0 -2986
  194. package/dist/commands/doctor.d.cts +0 -21
  195. package/dist/commands/embed.cjs +0 -232
  196. package/dist/commands/embed.d.cts +0 -17
  197. package/dist/commands/entities.cjs +0 -141
  198. package/dist/commands/entities.d.cts +0 -7
  199. package/dist/commands/graph.cjs +0 -501
  200. package/dist/commands/graph.d.cts +0 -21
  201. package/dist/commands/inject.cjs +0 -1636
  202. package/dist/commands/inject.d.cts +0 -2
  203. package/dist/commands/kanban.cjs +0 -884
  204. package/dist/commands/kanban.d.cts +0 -63
  205. package/dist/commands/link.cjs +0 -965
  206. package/dist/commands/link.d.cts +0 -11
  207. package/dist/commands/migrate-observations.cjs +0 -362
  208. package/dist/commands/migrate-observations.d.cts +0 -19
  209. package/dist/commands/observe.cjs +0 -4099
  210. package/dist/commands/observe.d.cts +0 -23
  211. package/dist/commands/project.cjs +0 -1341
  212. package/dist/commands/project.d.cts +0 -85
  213. package/dist/commands/rebuild.cjs +0 -3136
  214. package/dist/commands/rebuild.d.cts +0 -11
  215. package/dist/commands/recover.cjs +0 -361
  216. package/dist/commands/recover.d.cts +0 -38
  217. package/dist/commands/reflect.cjs +0 -1008
  218. package/dist/commands/reflect.d.cts +0 -11
  219. package/dist/commands/repair-session.cjs +0 -457
  220. package/dist/commands/repair-session.d.cts +0 -38
  221. package/dist/commands/replay.cjs +0 -4103
  222. package/dist/commands/replay.d.cts +0 -16
  223. package/dist/commands/session-recap.cjs +0 -353
  224. package/dist/commands/session-recap.d.cts +0 -27
  225. package/dist/commands/setup.cjs +0 -1278
  226. package/dist/commands/setup.d.cts +0 -99
  227. package/dist/commands/shell-init.cjs +0 -75
  228. package/dist/commands/shell-init.d.cts +0 -7
  229. package/dist/commands/sleep.cjs +0 -6029
  230. package/dist/commands/sleep.d.cts +0 -36
  231. package/dist/commands/status.cjs +0 -2737
  232. package/dist/commands/status.d.cts +0 -52
  233. package/dist/commands/task.cjs +0 -1236
  234. package/dist/commands/task.d.cts +0 -97
  235. package/dist/commands/template.cjs +0 -457
  236. package/dist/commands/template.d.cts +0 -36
  237. package/dist/commands/wake.cjs +0 -2627
  238. package/dist/commands/wake.d.cts +0 -22
  239. package/dist/context-BUGaWpyL.d.cts +0 -46
  240. package/dist/index.cjs +0 -12373
  241. package/dist/index.d.cts +0 -854
  242. package/dist/inject-Bzi5E-By.d.cts +0 -137
  243. package/dist/lib/auto-linker.cjs +0 -176
  244. package/dist/lib/auto-linker.d.cts +0 -26
  245. package/dist/lib/config.cjs +0 -78
  246. package/dist/lib/config.d.cts +0 -11
  247. package/dist/lib/entity-index.cjs +0 -84
  248. package/dist/lib/entity-index.d.cts +0 -26
  249. package/dist/lib/project-utils.cjs +0 -864
  250. package/dist/lib/project-utils.d.cts +0 -97
  251. package/dist/lib/session-repair.cjs +0 -239
  252. package/dist/lib/session-repair.d.cts +0 -110
  253. package/dist/lib/session-utils.cjs +0 -209
  254. package/dist/lib/session-utils.d.cts +0 -63
  255. package/dist/lib/task-utils.cjs +0 -1137
  256. package/dist/lib/task-utils.d.cts +0 -208
  257. package/dist/lib/template-engine.cjs +0 -47
  258. package/dist/lib/template-engine.d.cts +0 -11
  259. package/dist/plugin/index.cjs +0 -1907
  260. package/dist/plugin/index.d.cts +0 -36
  261. package/dist/plugin/index.d.ts +0 -36
  262. package/dist/plugin/index.js +0 -572
  263. package/dist/plugin/inject.cjs +0 -356
  264. package/dist/plugin/inject.d.cts +0 -54
  265. package/dist/plugin/inject.d.ts +0 -54
  266. package/dist/plugin/inject.js +0 -17
  267. package/dist/plugin/observe.cjs +0 -631
  268. package/dist/plugin/observe.d.cts +0 -39
  269. package/dist/plugin/observe.d.ts +0 -39
  270. package/dist/plugin/observe.js +0 -18
  271. package/dist/plugin/templates.cjs +0 -593
  272. package/dist/plugin/templates.d.cts +0 -52
  273. package/dist/plugin/templates.d.ts +0 -52
  274. package/dist/plugin/templates.js +0 -25
  275. package/dist/plugin/types.cjs +0 -18
  276. package/dist/plugin/types.d.cts +0 -209
  277. package/dist/plugin/types.d.ts +0 -209
  278. package/dist/plugin/types.js +0 -0
  279. package/dist/plugin/vault.cjs +0 -927
  280. package/dist/plugin/vault.d.cts +0 -68
  281. package/dist/plugin/vault.d.ts +0 -68
  282. package/dist/plugin/vault.js +0 -22
  283. package/dist/types-Y2_Um2Ls.d.cts +0 -205
  284. package/templates/memory-event.md +0 -67
  285. package/templates/party.md +0 -63
  286. package/templates/primitive-registry.yaml +0 -551
  287. package/templates/run.md +0 -68
  288. package/templates/trigger.md +0 -68
  289. package/templates/workspace.md +0 -50
@@ -1,21 +1,11 @@
1
1
  import {
2
2
  listProjects
3
- } from "./chunk-62YTUT6J.js";
3
+ } from "./chunk-4PY655YM.js";
4
4
  import {
5
- listConfig,
6
- listRouteRules,
7
- matchRouteRule
8
- } from "./chunk-ITPEXLHA.js";
9
- import {
10
- resolveClaudeOAuthToken
11
- } from "./chunk-PAH27GSN.js";
12
- import {
13
- createBacklogItem,
14
- listBacklogItems,
15
- listTasks,
16
- updateBacklogItem,
17
- updateTask
18
- } from "./chunk-N2AXRYLC.js";
5
+ FactStore,
6
+ extractFactsLlm,
7
+ extractFactsRuleBased
8
+ } from "./chunk-BSJ6RIT7.js";
19
9
  import {
20
10
  DATE_HEADING_RE,
21
11
  inferObservationType,
@@ -24,7 +14,16 @@ import {
24
14
  parseObservationMarkdown,
25
15
  renderObservationMarkdown,
26
16
  renderScoredObservationLine
27
- } from "./chunk-QK3UCXWL.js";
17
+ } 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-NCKFNBHJ.js";
28
27
  import {
29
28
  ensureLedgerStructure,
30
29
  ensureParentDir,
@@ -33,16 +32,27 @@ import {
33
32
  getRawTranscriptPath,
34
33
  toDateKey
35
34
  } from "./chunk-Z2XBWN7A.js";
35
+ import {
36
+ createBacklogItem,
37
+ listBacklogItems,
38
+ listTasks,
39
+ updateBacklogItem,
40
+ updateTask
41
+ } from "./chunk-QWQ3TIKS.js";
36
42
 
37
43
  // src/observer/compressor.ts
38
44
  var OPENAI_BASE_URL = "https://api.openai.com/v1";
45
+ var XAI_BASE_URL = "https://api.x.ai/v1";
39
46
  var OLLAMA_BASE_URL = "http://localhost:11434/v1";
40
47
  var DEFAULT_PROVIDER_MODELS = {
41
- anthropic: "claude-haiku-4-5",
48
+ anthropic: "claude-3-5-haiku-latest",
42
49
  openai: "gpt-4o-mini",
43
50
  gemini: "gemini-2.0-flash",
51
+ xai: "grok-2-latest",
44
52
  "openai-compatible": "gpt-4o-mini",
45
- ollama: "llama3.2"
53
+ ollama: "llama3.2",
54
+ minimax: "MiniMax-M2.1",
55
+ zai: "glm-4.5-air"
46
56
  };
47
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;
48
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;
@@ -51,6 +61,11 @@ var TODO_SIGNAL_RE = /(?:\btodo:\s*|\bwe need to\b|\bdon't forget(?: to)?\b|\bre
51
61
  var COMMITMENT_TASK_SIGNAL_RE = /\b(?:i'?ll|i will|let me|(?:i'?m\s+)?going to|plan to|should)\b/i;
52
62
  var UNRESOLVED_COMMITMENT_RE = /\b(?:need to figure out|tbd|to be determined)\b/i;
53
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 ROLE_PREFIX_RE = /^([a-z][a-z0-9_-]{1,31})\s*:\s*(.+)$/i;
65
+ var BASE64_DATA_URI_RE = /\bdata:[^;\s]+;base64,[A-Za-z0-9+/=]{24,}\b/gi;
66
+ var LONG_BASE64_TOKEN_RE = /\b[A-Za-z0-9+/]{80,}={0,2}\b/g;
67
+ var NOISE_PREFIX_RE = /^(?:metadata|system metadata|session metadata|tool[_-]?result|toolresult)\s*:/i;
68
+ var STRUCTURED_NOISE_MARKER_RE = /\b(?:tool[_-]?result|tool[_-]?use|toolcallid|tooluseid|function[_-]?(?:call|result)|stdout|stderr|exitcode|recordedat|trace(?:_|-)?id|parent(?:_|-)?id|session(?:_|-)?id|metadata|base64|mime(?:type)?)\b/i;
54
69
  var Compressor = class {
55
70
  provider;
56
71
  model;
@@ -67,15 +82,15 @@ var Compressor = class {
67
82
  this.fetchImpl = options.fetchImpl ?? fetch;
68
83
  }
69
84
  async compress(messages, existingObservations) {
70
- const cleanedMessages = messages.map((message) => message.trim()).filter(Boolean);
85
+ const cleanedMessages = this.sanitizeIncomingMessages(messages);
71
86
  if (cleanedMessages.length === 0) {
72
87
  return existingObservations.trim();
73
88
  }
74
89
  const prompt = this.buildPrompt(cleanedMessages, existingObservations);
75
- const backend = await this.resolveProvider();
90
+ const backend = this.resolveProvider();
76
91
  if (backend) {
77
92
  try {
78
- 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) : await this.callOpenAICompatible(prompt, backend);
93
+ 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);
79
94
  const normalized = this.normalizeLlmOutput(llmOutput);
80
95
  if (normalized) {
81
96
  return this.mergeObservations(existingObservations, normalized);
@@ -86,16 +101,100 @@ var Compressor = class {
86
101
  const fallback = this.fallbackCompression(cleanedMessages);
87
102
  return this.mergeObservations(existingObservations, fallback);
88
103
  }
89
- async resolveProvider() {
104
+ sanitizeIncomingMessages(messages) {
105
+ const sanitized = [];
106
+ for (const message of messages) {
107
+ const cleaned = this.sanitizeIncomingMessage(message);
108
+ if (cleaned) {
109
+ sanitized.push(cleaned);
110
+ }
111
+ }
112
+ return sanitized;
113
+ }
114
+ sanitizeIncomingMessage(message) {
115
+ const normalized = message.replace(/\s+/g, " ").trim();
116
+ if (!normalized) {
117
+ return "";
118
+ }
119
+ const sourceTagMatch = /^\[[^\]]+\]\s+/.exec(normalized);
120
+ const sourceTag = sourceTagMatch ? sourceTagMatch[0] : "";
121
+ const payload = sourceTag ? normalized.slice(sourceTag.length).trimStart() : normalized;
122
+ const roleMatch = ROLE_PREFIX_RE.exec(payload);
123
+ if (roleMatch && this.isConversationRolePrefix(roleMatch[1])) {
124
+ const role = this.normalizeMessageRole(roleMatch[1]);
125
+ if (this.shouldDropMessageRole(role)) {
126
+ return "";
127
+ }
128
+ const content = this.stripNoisyData(roleMatch[2]);
129
+ if (!content || this.isLikelyStructuredNoise(content)) {
130
+ return "";
131
+ }
132
+ const cleaned2 = `${role}: ${content}`;
133
+ return sourceTag ? `${sourceTag}${cleaned2}` : cleaned2;
134
+ }
135
+ const cleaned = this.stripNoisyData(payload);
136
+ if (!cleaned || this.isLikelyStructuredNoise(cleaned)) {
137
+ return "";
138
+ }
139
+ return sourceTag ? `${sourceTag}${cleaned}` : cleaned;
140
+ }
141
+ normalizeMessageRole(role) {
142
+ return role.trim().toLowerCase();
143
+ }
144
+ isConversationRolePrefix(role) {
145
+ const normalized = role.trim().toLowerCase().replace(/[\s_-]+/g, "");
146
+ if (!normalized) {
147
+ return false;
148
+ }
149
+ if (normalized === "user" || normalized === "assistant" || normalized === "system") {
150
+ return true;
151
+ }
152
+ if (normalized === "developer" || normalized === "metadata") {
153
+ return true;
154
+ }
155
+ return normalized.startsWith("tool");
156
+ }
157
+ shouldDropMessageRole(role) {
158
+ const normalized = role.replace(/[\s_-]+/g, "");
159
+ if (!normalized) {
160
+ return false;
161
+ }
162
+ if (normalized === "system" || normalized === "developer" || normalized === "metadata") {
163
+ return true;
164
+ }
165
+ return normalized.startsWith("tool");
166
+ }
167
+ stripNoisyData(value) {
168
+ return value.replace(BASE64_DATA_URI_RE, " ").replace(LONG_BASE64_TOKEN_RE, " ").replace(/\s+/g, " ").trim();
169
+ }
170
+ isLikelyStructuredNoise(value) {
171
+ const trimmed = value.trim();
172
+ if (!trimmed) {
173
+ return true;
174
+ }
175
+ if (NOISE_PREFIX_RE.test(trimmed)) {
176
+ return true;
177
+ }
178
+ const afterToolResult = trimmed.match(/^tool[_-]?result\s*:\s*(\{.*|\[.*)/i);
179
+ if (afterToolResult && STRUCTURED_NOISE_MARKER_RE.test(trimmed) && trimmed.length >= 40) {
180
+ return true;
181
+ }
182
+ const looksStructured = trimmed.startsWith("{") || trimmed.startsWith("[");
183
+ if (looksStructured && STRUCTURED_NOISE_MARKER_RE.test(trimmed) && trimmed.length >= 40) {
184
+ return true;
185
+ }
186
+ return false;
187
+ }
188
+ resolveProvider() {
90
189
  if (process.env.CLAWVAULT_NO_LLM) return null;
91
190
  if (this.provider) {
92
191
  const configured = this.resolveConfiguredProvider(this.provider);
93
192
  if (configured) {
94
193
  return configured;
95
194
  }
96
- return await this.resolveProviderFromEnv(false);
195
+ return this.resolveProviderFromEnv(false);
97
196
  }
98
- return await this.resolveProviderFromEnv(true);
197
+ return this.resolveProviderFromEnv(true);
99
198
  }
100
199
  resolveConfiguredProvider(provider) {
101
200
  const model = this.resolveModel(provider);
@@ -133,6 +232,18 @@ var Compressor = class {
133
232
  baseUrl: this.resolveBaseUrl(provider)
134
233
  };
135
234
  }
235
+ if (provider === "xai") {
236
+ const apiKey2 = this.resolveApiKey(provider);
237
+ if (!apiKey2) {
238
+ return null;
239
+ }
240
+ return {
241
+ provider,
242
+ model,
243
+ apiKey: apiKey2,
244
+ baseUrl: XAI_BASE_URL
245
+ };
246
+ }
136
247
  const apiKey = this.resolveApiKey(provider) ?? void 0;
137
248
  return {
138
249
  provider,
@@ -141,21 +252,14 @@ var Compressor = class {
141
252
  baseUrl: this.resolveBaseUrl(provider)
142
253
  };
143
254
  }
144
- async resolveProviderFromEnv(allowConfiguredModel) {
145
- const anthropicModel = allowConfiguredModel ? this.resolveModel("anthropic") : DEFAULT_PROVIDER_MODELS.anthropic;
146
- const oauthEnvToken = this.readEnvValue("ANTHROPIC_OAUTH_TOKEN");
147
- if (oauthEnvToken) {
148
- return { provider: "anthropic", model: anthropicModel, apiKey: oauthEnvToken, isOAuth: true };
149
- }
255
+ resolveProviderFromEnv(allowConfiguredModel) {
150
256
  const anthropicApiKey = this.readEnvValue("ANTHROPIC_API_KEY");
151
257
  if (anthropicApiKey) {
152
- return { provider: "anthropic", model: anthropicModel, apiKey: anthropicApiKey };
153
- }
154
- if (this.readEnvValue("CLAWVAULT_CLAUDE_AUTH")) {
155
- const oauthToken = await resolveClaudeOAuthToken();
156
- if (oauthToken) {
157
- return { provider: "anthropic", model: anthropicModel, apiKey: oauthToken, isOAuth: true };
158
- }
258
+ return {
259
+ provider: "anthropic",
260
+ model: allowConfiguredModel ? this.resolveModel("anthropic") : DEFAULT_PROVIDER_MODELS.anthropic,
261
+ apiKey: anthropicApiKey
262
+ };
159
263
  }
160
264
  const openAiApiKey = this.readEnvValue("OPENAI_API_KEY");
161
265
  if (openAiApiKey) {
@@ -174,6 +278,15 @@ var Compressor = class {
174
278
  apiKey: geminiApiKey
175
279
  };
176
280
  }
281
+ const xaiApiKey = this.readEnvValue("XAI_API_KEY");
282
+ if (xaiApiKey) {
283
+ return {
284
+ provider: "xai",
285
+ model: allowConfiguredModel ? this.resolveModel("xai") : DEFAULT_PROVIDER_MODELS.xai,
286
+ apiKey: xaiApiKey,
287
+ baseUrl: XAI_BASE_URL
288
+ };
289
+ }
177
290
  return null;
178
291
  }
179
292
  resolveModel(provider) {
@@ -194,6 +307,9 @@ var Compressor = class {
194
307
  if (provider === "gemini") {
195
308
  return this.readEnvValue("GEMINI_API_KEY");
196
309
  }
310
+ if (provider === "xai") {
311
+ return this.readEnvValue("XAI_API_KEY");
312
+ }
197
313
  return this.readEnvValue("OPENAI_API_KEY");
198
314
  }
199
315
  resolveBaseUrl(provider) {
@@ -225,17 +341,6 @@ var Compressor = class {
225
341
  "- Confidence c reflects extraction certainty, not importance.",
226
342
  "- Preserve source tags when present (e.g., [main], [telegram-dm], [discord], [telegram-group]).",
227
343
  "",
228
- "PREFERENCE & PERSONAL CONTEXT EXTRACTION (critical for personalization):",
229
- "- Emit [preference] for ANY personal detail that reveals tastes, habits, equipment, or context:",
230
- ' * Explicit: "I prefer X", "I like Y", "I always use Z"',
231
- ' * Ownership: "my Sony A7R IV", "I have a...", "I use...", "I own..."',
232
- ' * Habits/routines: "I usually...", "every morning I...", "I tend to..."',
233
- " * Interests: topics the user is enthusiastic about, hobbies mentioned in passing",
234
- " * Constraints: dietary restrictions, allergies, phobias, limitations",
235
- ` * Goals: "I'm trying to...", "I want to learn..."`,
236
- "- These are HIGH VALUE observations (i >= 0.60) \u2014 they enable personalized responses.",
237
- "- When in doubt between [preference] and [task], choose [preference] if it describes a lasting trait.",
238
- "",
239
344
  "TASK EXTRACTION (required):",
240
345
  `- Emit [todo] for explicit TODO phrasing: "TODO:", "we need to", "don't forget", "remember to", "make sure to".`,
241
346
  `- Emit [task] for commitments/action intent: "I'll", "I will", "let me", "going to", "plan to", "should".`,
@@ -314,35 +419,19 @@ var Compressor = class {
314
419
  if (!backend.apiKey) {
315
420
  return "";
316
421
  }
317
- const isOAuth = backend.isOAuth || backend.apiKey.includes("sk-ant-oat");
318
- const headers = isOAuth ? {
319
- "content-type": "application/json",
320
- "authorization": `Bearer ${backend.apiKey}`,
321
- "anthropic-version": "2023-06-01",
322
- "anthropic-beta": "claude-code-20250219,oauth-2025-04-20",
323
- "x-app": "cli",
324
- "user-agent": "claude-cli/1.0.0 (external, cli)"
325
- } : {
326
- "content-type": "application/json",
327
- "x-api-key": backend.apiKey,
328
- "anthropic-version": "2023-06-01"
329
- };
330
- const body = isOAuth ? {
331
- model: backend.model,
332
- temperature: 0.1,
333
- max_tokens: 1400,
334
- system: [{ type: "text", text: "You are Claude Code, Anthropic's official CLI for Claude." }],
335
- messages: [{ role: "user", content: prompt }]
336
- } : {
337
- model: backend.model,
338
- temperature: 0.1,
339
- max_tokens: 1400,
340
- messages: [{ role: "user", content: prompt }]
341
- };
342
422
  const response = await this.fetchImpl("https://api.anthropic.com/v1/messages", {
343
423
  method: "POST",
344
- headers,
345
- body: JSON.stringify(body)
424
+ headers: {
425
+ "content-type": "application/json",
426
+ "x-api-key": backend.apiKey,
427
+ "anthropic-version": "2023-06-01"
428
+ },
429
+ body: JSON.stringify({
430
+ model: backend.model,
431
+ temperature: 0.1,
432
+ max_tokens: 1400,
433
+ messages: [{ role: "user", content: prompt }]
434
+ })
346
435
  });
347
436
  if (!response.ok) {
348
437
  throw new Error(`Anthropic request failed (${response.status})`);
@@ -353,6 +442,9 @@ var Compressor = class {
353
442
  async callOpenAI(prompt, backend) {
354
443
  return this.callOpenAICompatible(prompt, backend);
355
444
  }
445
+ async callXAI(prompt, backend) {
446
+ return this.callOpenAICompatible(prompt, backend);
447
+ }
356
448
  async callOpenAICompatible(prompt, backend) {
357
449
  const baseUrl = backend.baseUrl ?? this.resolveBaseUrl(backend.provider);
358
450
  const response = await this.fetchImpl(this.buildOpenAICompatibleUrl(baseUrl), {
@@ -728,6 +820,125 @@ var Reflector = class {
728
820
  }
729
821
  };
730
822
 
823
+ // src/lib/llm-adapter.ts
824
+ var GEMINI_FLASH_MODEL = "gemini-2.0-flash";
825
+ var OLLAMA_DEFAULT_MODEL = "llama3.1:8b";
826
+ var OLLAMA_BASE_URL2 = "http://127.0.0.1:11434";
827
+ function createGeminiFlashAdapter(options = {}) {
828
+ const apiKey = process.env.GEMINI_API_KEY;
829
+ return {
830
+ async call(prompt) {
831
+ if (!apiKey) {
832
+ return "";
833
+ }
834
+ return requestLlmCompletion({
835
+ prompt,
836
+ provider: "gemini",
837
+ model: options.model ?? GEMINI_FLASH_MODEL,
838
+ temperature: options.temperature ?? 0.1,
839
+ maxTokens: options.maxTokens ?? 2e3,
840
+ fetchImpl: options.fetchImpl
841
+ });
842
+ },
843
+ isAvailable() {
844
+ return Boolean(apiKey);
845
+ },
846
+ getProvider() {
847
+ return apiKey ? "gemini" : null;
848
+ }
849
+ };
850
+ }
851
+ function createOllamaAdapter(options = {}) {
852
+ let _available = null;
853
+ const fetchFn = options.fetchImpl ?? globalThis.fetch;
854
+ return {
855
+ async call(prompt) {
856
+ const resp = await fetchFn(`${OLLAMA_BASE_URL2}/api/generate`, {
857
+ method: "POST",
858
+ headers: { "Content-Type": "application/json" },
859
+ body: JSON.stringify({
860
+ model: options.model ?? OLLAMA_DEFAULT_MODEL,
861
+ prompt,
862
+ stream: false,
863
+ options: {
864
+ temperature: options.temperature ?? 0.1,
865
+ num_predict: options.maxTokens ?? 2e3
866
+ }
867
+ })
868
+ });
869
+ if (!resp.ok) return "";
870
+ const data = await resp.json();
871
+ return data.response ?? "";
872
+ },
873
+ isAvailable() {
874
+ if (_available !== null) return _available;
875
+ _available = true;
876
+ return true;
877
+ },
878
+ getProvider() {
879
+ return null;
880
+ }
881
+ };
882
+ }
883
+ function createDefaultAdapter(options = {}) {
884
+ const resolvedProvider = options.provider !== void 0 ? options.provider : resolveLlmProvider();
885
+ return {
886
+ async call(prompt) {
887
+ if (!resolvedProvider) {
888
+ return "";
889
+ }
890
+ return requestLlmCompletion({
891
+ prompt,
892
+ provider: resolvedProvider,
893
+ model: options.model,
894
+ temperature: options.temperature ?? 0.1,
895
+ maxTokens: options.maxTokens ?? 2e3,
896
+ fetchImpl: options.fetchImpl
897
+ });
898
+ },
899
+ isAvailable() {
900
+ return resolvedProvider !== null;
901
+ },
902
+ getProvider() {
903
+ return resolvedProvider;
904
+ }
905
+ };
906
+ }
907
+ function createFactExtractionAdapter(options = {}) {
908
+ if (options.provider) {
909
+ return createDefaultAdapter(options);
910
+ }
911
+ const geminiAdapter = createGeminiFlashAdapter(options);
912
+ if (geminiAdapter.isAvailable()) {
913
+ return geminiAdapter;
914
+ }
915
+ const ollamaAdapter = createOllamaAdapter(options);
916
+ if (ollamaAdapter.isAvailable()) {
917
+ return ollamaAdapter;
918
+ }
919
+ return createDefaultAdapter(options);
920
+ }
921
+ function createLlmFunction(adapter) {
922
+ if (!adapter.isAvailable()) {
923
+ return void 0;
924
+ }
925
+ return (prompt) => adapter.call(prompt);
926
+ }
927
+ function resolveFactExtractionMode(configuredMode, adapter) {
928
+ const mode = configuredMode ?? "llm";
929
+ if (mode === "off") {
930
+ return { mode: "off", useLlm: false };
931
+ }
932
+ if (mode === "rule") {
933
+ return { mode: "rule", useLlm: false };
934
+ }
935
+ const llmAvailable = adapter?.isAvailable() ?? resolveLlmProvider() !== null;
936
+ if (mode === "llm" || mode === "hybrid") {
937
+ return { mode, useLlm: llmAvailable };
938
+ }
939
+ return { mode: "rule", useLlm: false };
940
+ }
941
+
731
942
  // src/observer/observer.ts
732
943
  import * as fs2 from "fs";
733
944
  import * as path2 from "path";
@@ -801,17 +1012,41 @@ var FUTURE_TASK_HINT_RE = /\b(need to|should|todo|must|plan to)\b/i;
801
1012
  var Router = class {
802
1013
  vaultPath;
803
1014
  extractTasks;
1015
+ extractFacts;
1016
+ factExtractionMode;
1017
+ llmAdapter;
1018
+ factStore;
804
1019
  now;
805
1020
  customRoutes;
806
1021
  constructor(vaultPath, options = {}) {
807
1022
  this.vaultPath = path.resolve(vaultPath);
808
1023
  this.extractTasks = options.extractTasks ?? true;
1024
+ this.extractFacts = options.extractFacts ?? true;
1025
+ this.factExtractionMode = options.factExtractionMode ?? this.loadFactExtractionMode();
1026
+ this.llmAdapter = options.llmAdapter ?? createFactExtractionAdapter();
1027
+ this.factStore = new FactStore(this.vaultPath);
809
1028
  this.now = options.now ?? (() => /* @__PURE__ */ new Date());
810
1029
  this.customRoutes = this.loadCustomRoutes();
1030
+ if (this.extractFacts && this.factExtractionMode !== "off") {
1031
+ this.factStore.load();
1032
+ }
1033
+ }
1034
+ loadFactExtractionMode() {
1035
+ try {
1036
+ const config = listConfig(this.vaultPath);
1037
+ const observer = config.observer;
1038
+ const mode = observer?.factExtractionMode;
1039
+ if (mode === "off" || mode === "rule" || mode === "llm" || mode === "hybrid") {
1040
+ return mode;
1041
+ }
1042
+ } catch {
1043
+ }
1044
+ return "llm";
811
1045
  }
812
1046
  /**
813
1047
  * Takes observation markdown and routes items to appropriate vault categories.
814
1048
  * Routes only items with importance >= 0.4.
1049
+ * Also extracts structured facts from observations when fact extraction is enabled.
815
1050
  * Returns a summary of what was routed where.
816
1051
  */
817
1052
  route(observationMarkdown, context = {}) {
@@ -821,6 +1056,7 @@ var Router = class {
821
1056
  const knownWorkItems = this.extractTasks ? this.loadExistingWorkItems() : [];
822
1057
  const knownProjectDefinitions = this.loadKnownProjectDefinitions();
823
1058
  let dedupHits = 0;
1059
+ let factsExtracted = 0;
824
1060
  for (const item of items) {
825
1061
  if (item.importance < 0.4) continue;
826
1062
  if (this.extractTasks && this.isTaskObservation(item.type)) {
@@ -847,8 +1083,81 @@ var Router = class {
847
1083
  routed.push(routedItem);
848
1084
  this.appendToCategory(category, routedItem, knownProjectDefinitions);
849
1085
  }
850
- const summary = this.buildSummary(routed, dedupHits);
851
- return { routed, summary };
1086
+ if (this.extractFacts && this.factExtractionMode !== "off") {
1087
+ const extractedCount = this.extractAndStoreFacts(observationMarkdown, context);
1088
+ factsExtracted = extractedCount;
1089
+ }
1090
+ const summary = this.buildSummary(routed, dedupHits, factsExtracted);
1091
+ return { routed, summary, factsExtracted };
1092
+ }
1093
+ /**
1094
+ * Extract facts from observation markdown and store them in the fact store.
1095
+ * Uses the configured extraction mode (rule-based, LLM, or hybrid).
1096
+ */
1097
+ extractAndStoreFacts(observationMarkdown, context) {
1098
+ const { mode, useLlm } = resolveFactExtractionMode(this.factExtractionMode, this.llmAdapter);
1099
+ if (mode === "off") {
1100
+ return 0;
1101
+ }
1102
+ const source = context.source ?? "observer";
1103
+ const timestamp = context.timestamp?.toISOString() ?? this.now().toISOString();
1104
+ let facts = [];
1105
+ if (mode === "rule" || !useLlm) {
1106
+ facts = extractFactsRuleBased(observationMarkdown, source, timestamp);
1107
+ } else if (mode === "llm") {
1108
+ const llmFn = createLlmFunction(this.llmAdapter);
1109
+ extractFactsLlm(observationMarkdown, source, timestamp, llmFn).then((llmFacts) => {
1110
+ if (llmFacts.length > 0) {
1111
+ this.factStore.addFacts(llmFacts);
1112
+ this.factStore.save();
1113
+ }
1114
+ }).catch(() => {
1115
+ const ruleFacts = extractFactsRuleBased(observationMarkdown, source, timestamp);
1116
+ if (ruleFacts.length > 0) {
1117
+ this.factStore.addFacts(ruleFacts);
1118
+ this.factStore.save();
1119
+ }
1120
+ });
1121
+ return 0;
1122
+ } else if (mode === "hybrid") {
1123
+ facts = extractFactsRuleBased(observationMarkdown, source, timestamp);
1124
+ const llmFn = createLlmFunction(this.llmAdapter);
1125
+ extractFactsLlm(observationMarkdown, source, timestamp, llmFn).then((llmFacts) => {
1126
+ const merged = this.mergeFacts(facts, llmFacts);
1127
+ if (merged.length > facts.length) {
1128
+ this.factStore.addFacts(merged.slice(facts.length));
1129
+ this.factStore.save();
1130
+ }
1131
+ }).catch(() => {
1132
+ });
1133
+ }
1134
+ if (facts.length > 0) {
1135
+ this.factStore.addFacts(facts);
1136
+ this.factStore.save();
1137
+ }
1138
+ return facts.length;
1139
+ }
1140
+ /**
1141
+ * Merge facts from rule-based and LLM extraction, deduplicating by entity+relation.
1142
+ */
1143
+ mergeFacts(ruleFacts, llmFacts) {
1144
+ const seen = /* @__PURE__ */ new Set();
1145
+ const merged = [];
1146
+ for (const fact of ruleFacts) {
1147
+ const key = `${fact.entityNorm}::${fact.relation}`;
1148
+ if (!seen.has(key)) {
1149
+ seen.add(key);
1150
+ merged.push(fact);
1151
+ }
1152
+ }
1153
+ for (const fact of llmFacts) {
1154
+ const key = `${fact.entityNorm}::${fact.relation}`;
1155
+ if (!seen.has(key)) {
1156
+ seen.add(key);
1157
+ merged.push(fact);
1158
+ }
1159
+ }
1160
+ return merged;
852
1161
  }
853
1162
  isTaskObservation(type) {
854
1163
  return type === "task" || type === "todo" || type === "commitment-unresolved";
@@ -1436,8 +1745,8 @@ ${entry}
1436
1745
  for (const bg of setA) if (setB.has(bg)) intersection++;
1437
1746
  return intersection / (setA.size + setB.size - intersection);
1438
1747
  }
1439
- buildSummary(routed, dedupHits) {
1440
- if (routed.length === 0) {
1748
+ buildSummary(routed, dedupHits, factsExtracted = 0) {
1749
+ if (routed.length === 0 && factsExtracted === 0) {
1441
1750
  if (dedupHits > 0) {
1442
1751
  return `No items routed to vault categories (dedup hits: ${dedupHits}).`;
1443
1752
  }
@@ -1448,7 +1757,17 @@ ${entry}
1448
1757
  byCat.set(item.category, (byCat.get(item.category) ?? 0) + 1);
1449
1758
  }
1450
1759
  const parts = [...byCat.entries()].map(([cat, count]) => `${cat}: ${count}`);
1451
- const suffix = dedupHits > 0 ? ` (dedup hits: ${dedupHits})` : "";
1760
+ const suffixParts = [];
1761
+ if (dedupHits > 0) {
1762
+ suffixParts.push(`dedup hits: ${dedupHits}`);
1763
+ }
1764
+ if (factsExtracted > 0) {
1765
+ suffixParts.push(`facts: ${factsExtracted}`);
1766
+ }
1767
+ const suffix = suffixParts.length > 0 ? ` (${suffixParts.join(", ")})` : "";
1768
+ if (routed.length === 0 && factsExtracted > 0) {
1769
+ return `Extracted ${factsExtracted} facts${suffix}`;
1770
+ }
1452
1771
  return `Routed ${routed.length} observations \u2192 ${parts.join(", ")}${suffix}`;
1453
1772
  }
1454
1773
  };
@@ -1698,5 +2017,10 @@ var Observer = class {
1698
2017
  export {
1699
2018
  Compressor,
1700
2019
  Reflector,
2020
+ createGeminiFlashAdapter,
2021
+ createDefaultAdapter,
2022
+ createFactExtractionAdapter,
2023
+ createLlmFunction,
2024
+ resolveFactExtractionMode,
1701
2025
  Observer
1702
2026
  };