verybot 0.1.8

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 (277) hide show
  1. package/README.md +167 -0
  2. package/dist/aliases/store.d.ts +21 -0
  3. package/dist/aliases/store.js +148 -0
  4. package/dist/aliases/types.d.ts +6 -0
  5. package/dist/aliases/types.js +1 -0
  6. package/dist/brain/agent-registry.d.ts +96 -0
  7. package/dist/brain/agent-registry.js +141 -0
  8. package/dist/brain/agent.d.ts +167 -0
  9. package/dist/brain/agent.js +932 -0
  10. package/dist/brain/channel-store.d.ts +27 -0
  11. package/dist/brain/channel-store.js +78 -0
  12. package/dist/brain/compaction.d.ts +37 -0
  13. package/dist/brain/compaction.js +214 -0
  14. package/dist/brain/context.d.ts +43 -0
  15. package/dist/brain/context.js +139 -0
  16. package/dist/brain/delegation-store.d.ts +33 -0
  17. package/dist/brain/delegation-store.js +106 -0
  18. package/dist/brain/loop.d.ts +24 -0
  19. package/dist/brain/loop.js +318 -0
  20. package/dist/brain/mcp-adapter.d.ts +43 -0
  21. package/dist/brain/mcp-adapter.js +244 -0
  22. package/dist/brain/memory-extractor.d.ts +26 -0
  23. package/dist/brain/memory-extractor.js +82 -0
  24. package/dist/brain/providers.d.ts +14 -0
  25. package/dist/brain/providers.js +85 -0
  26. package/dist/brain/queue.d.ts +18 -0
  27. package/dist/brain/queue.js +111 -0
  28. package/dist/brain/run-tools.d.ts +50 -0
  29. package/dist/brain/run-tools.js +136 -0
  30. package/dist/brain/session-key.d.ts +23 -0
  31. package/dist/brain/session-key.js +41 -0
  32. package/dist/brain/session-state.d.ts +36 -0
  33. package/dist/brain/session-state.js +51 -0
  34. package/dist/brain/session-store.d.ts +50 -0
  35. package/dist/brain/session-store.js +207 -0
  36. package/dist/brain/session.d.ts +32 -0
  37. package/dist/brain/session.js +75 -0
  38. package/dist/brain/task-subscriber.d.ts +56 -0
  39. package/dist/brain/task-subscriber.js +317 -0
  40. package/dist/brain/user-content.d.ts +16 -0
  41. package/dist/brain/user-content.js +32 -0
  42. package/dist/brain/utils.d.ts +4 -0
  43. package/dist/brain/utils.js +26 -0
  44. package/dist/brain/worker-coordinator.d.ts +25 -0
  45. package/dist/brain/worker-coordinator.js +83 -0
  46. package/dist/channels/commands.d.ts +50 -0
  47. package/dist/channels/commands.js +132 -0
  48. package/dist/channels/discord/channel.d.ts +29 -0
  49. package/dist/channels/discord/channel.js +159 -0
  50. package/dist/channels/discord/markdown.d.ts +19 -0
  51. package/dist/channels/discord/markdown.js +62 -0
  52. package/dist/channels/manager.d.ts +29 -0
  53. package/dist/channels/manager.js +100 -0
  54. package/dist/channels/slack/channel.d.ts +37 -0
  55. package/dist/channels/slack/channel.js +227 -0
  56. package/dist/channels/slack/markdown.d.ts +19 -0
  57. package/dist/channels/slack/markdown.js +62 -0
  58. package/dist/channels/specs.d.ts +32 -0
  59. package/dist/channels/specs.js +99 -0
  60. package/dist/channels/telegram/channel.d.ts +29 -0
  61. package/dist/channels/telegram/channel.js +182 -0
  62. package/dist/channels/telegram/markdown.d.ts +17 -0
  63. package/dist/channels/telegram/markdown.js +66 -0
  64. package/dist/channels/types.d.ts +26 -0
  65. package/dist/channels/types.js +1 -0
  66. package/dist/channels/whatsapp/channel.d.ts +34 -0
  67. package/dist/channels/whatsapp/channel.js +276 -0
  68. package/dist/channels/whatsapp/markdown.d.ts +20 -0
  69. package/dist/channels/whatsapp/markdown.js +51 -0
  70. package/dist/cli/claude-login.d.ts +5 -0
  71. package/dist/cli/claude-login.js +47 -0
  72. package/dist/cli/config.d.ts +5 -0
  73. package/dist/cli/config.js +78 -0
  74. package/dist/cli/index.d.ts +11 -0
  75. package/dist/cli/index.js +96 -0
  76. package/dist/computer/browser/actions.d.ts +31 -0
  77. package/dist/computer/browser/actions.js +148 -0
  78. package/dist/computer/browser/context-manager.d.ts +28 -0
  79. package/dist/computer/browser/context-manager.js +78 -0
  80. package/dist/computer/browser/manager.d.ts +91 -0
  81. package/dist/computer/browser/manager.js +344 -0
  82. package/dist/computer/browser/profile-badge.d.ts +13 -0
  83. package/dist/computer/browser/profile-badge.js +67 -0
  84. package/dist/computer/browser/screenshot.d.ts +5 -0
  85. package/dist/computer/browser/screenshot.js +21 -0
  86. package/dist/computer/browser/snapshot.d.ts +30 -0
  87. package/dist/computer/browser/snapshot.js +242 -0
  88. package/dist/computer/browser/tools.d.ts +5 -0
  89. package/dist/computer/browser/tools.js +167 -0
  90. package/dist/computer/browser/types.d.ts +26 -0
  91. package/dist/computer/browser/types.js +1 -0
  92. package/dist/computer/desktop/adapter.d.ts +25 -0
  93. package/dist/computer/desktop/adapter.js +11 -0
  94. package/dist/computer/desktop/macos.d.ts +24 -0
  95. package/dist/computer/desktop/macos.js +223 -0
  96. package/dist/computer/desktop/tools.d.ts +25 -0
  97. package/dist/computer/desktop/tools.js +114 -0
  98. package/dist/config/agent-config.d.ts +55 -0
  99. package/dist/config/agent-config.js +16 -0
  100. package/dist/config/model-catalog.d.ts +22 -0
  101. package/dist/config/model-catalog.js +112 -0
  102. package/dist/config/model-spec.d.ts +8 -0
  103. package/dist/config/model-spec.js +66 -0
  104. package/dist/config/store.d.ts +25 -0
  105. package/dist/config/store.js +143 -0
  106. package/dist/config.d.ts +110 -0
  107. package/dist/config.js +259 -0
  108. package/dist/control-ui/assets/index-Cbl7G5Sc.css +1 -0
  109. package/dist/control-ui/assets/index-Cu1P4C62.js +266 -0
  110. package/dist/control-ui/assets/noto-sans-cyrillic-ext-wght-normal-DSNfmdVt.woff2 +0 -0
  111. package/dist/control-ui/assets/noto-sans-cyrillic-wght-normal-B2hlT84T.woff2 +0 -0
  112. package/dist/control-ui/assets/noto-sans-devanagari-wght-normal-Cv-Vwajv.woff2 +0 -0
  113. package/dist/control-ui/assets/noto-sans-greek-ext-wght-normal-12T8GTDR.woff2 +0 -0
  114. package/dist/control-ui/assets/noto-sans-greek-wght-normal-Ymb6dZNd.woff2 +0 -0
  115. package/dist/control-ui/assets/noto-sans-latin-ext-wght-normal-W1qJv59z.woff2 +0 -0
  116. package/dist/control-ui/assets/noto-sans-latin-wght-normal-BYSzYMf3.woff2 +0 -0
  117. package/dist/control-ui/assets/noto-sans-vietnamese-wght-normal-DLTJy58D.woff2 +0 -0
  118. package/dist/control-ui/index.html +14 -0
  119. package/dist/control-ui/vite.svg +1 -0
  120. package/dist/events.d.ts +2 -0
  121. package/dist/events.js +11 -0
  122. package/dist/gateway/broadcast.d.ts +5 -0
  123. package/dist/gateway/broadcast.js +33 -0
  124. package/dist/gateway/methods/aliases.d.ts +17 -0
  125. package/dist/gateway/methods/aliases.js +22 -0
  126. package/dist/gateway/methods/chat.d.ts +33 -0
  127. package/dist/gateway/methods/chat.js +37 -0
  128. package/dist/gateway/methods/config.d.ts +14 -0
  129. package/dist/gateway/methods/config.js +24 -0
  130. package/dist/gateway/methods/models.d.ts +10 -0
  131. package/dist/gateway/methods/models.js +14 -0
  132. package/dist/gateway/methods/playbooks.d.ts +45 -0
  133. package/dist/gateway/methods/playbooks.js +488 -0
  134. package/dist/gateway/methods/prompt-templates.d.ts +27 -0
  135. package/dist/gateway/methods/prompt-templates.js +106 -0
  136. package/dist/gateway/methods/scheduler.d.ts +62 -0
  137. package/dist/gateway/methods/scheduler.js +129 -0
  138. package/dist/gateway/methods/sessions.d.ts +44 -0
  139. package/dist/gateway/methods/sessions.js +111 -0
  140. package/dist/gateway/methods/system.d.ts +12 -0
  141. package/dist/gateway/methods/system.js +39 -0
  142. package/dist/gateway/methods/tasks.d.ts +40 -0
  143. package/dist/gateway/methods/tasks.js +151 -0
  144. package/dist/gateway/methods/teams.d.ts +69 -0
  145. package/dist/gateway/methods/teams.js +376 -0
  146. package/dist/gateway/methods/tools.d.ts +6 -0
  147. package/dist/gateway/methods/tools.js +7 -0
  148. package/dist/gateway/methods/whatsapp.d.ts +19 -0
  149. package/dist/gateway/methods/whatsapp.js +35 -0
  150. package/dist/gateway/rpc.d.ts +38 -0
  151. package/dist/gateway/rpc.js +79 -0
  152. package/dist/gateway/server.d.ts +9 -0
  153. package/dist/gateway/server.js +137 -0
  154. package/dist/index.d.ts +1 -0
  155. package/dist/index.js +254 -0
  156. package/dist/integrations/github.d.ts +7 -0
  157. package/dist/integrations/github.js +133 -0
  158. package/dist/integrations/mcp.d.ts +7 -0
  159. package/dist/integrations/mcp.js +106 -0
  160. package/dist/integrations/registry.d.ts +47 -0
  161. package/dist/integrations/registry.js +332 -0
  162. package/dist/integrations/scanner.d.ts +10 -0
  163. package/dist/integrations/scanner.js +122 -0
  164. package/dist/integrations/twitter.d.ts +10 -0
  165. package/dist/integrations/twitter.js +120 -0
  166. package/dist/integrations/types.d.ts +72 -0
  167. package/dist/integrations/types.js +1 -0
  168. package/dist/logger.d.ts +16 -0
  169. package/dist/logger.js +104 -0
  170. package/dist/markdown/chunk.d.ts +9 -0
  171. package/dist/markdown/chunk.js +52 -0
  172. package/dist/markdown/ir.d.ts +37 -0
  173. package/dist/markdown/ir.js +529 -0
  174. package/dist/markdown/render.d.ts +22 -0
  175. package/dist/markdown/render.js +148 -0
  176. package/dist/markdown/table-render.d.ts +43 -0
  177. package/dist/markdown/table-render.js +219 -0
  178. package/dist/markdown/tables.d.ts +17 -0
  179. package/dist/markdown/tables.js +27 -0
  180. package/dist/memory/embedding.d.ts +16 -0
  181. package/dist/memory/embedding.js +66 -0
  182. package/dist/memory/explicit.d.ts +16 -0
  183. package/dist/memory/explicit.js +29 -0
  184. package/dist/memory/extractor.d.ts +13 -0
  185. package/dist/memory/extractor.js +82 -0
  186. package/dist/memory/search.d.ts +15 -0
  187. package/dist/memory/search.js +57 -0
  188. package/dist/memory/session-learning.d.ts +23 -0
  189. package/dist/memory/session-learning.js +55 -0
  190. package/dist/memory/store.d.ts +36 -0
  191. package/dist/memory/store.js +334 -0
  192. package/dist/memory/types.d.ts +9 -0
  193. package/dist/memory/types.js +2 -0
  194. package/dist/paths.d.ts +28 -0
  195. package/dist/paths.js +48 -0
  196. package/dist/prompt-templates/builtins/index.d.ts +4 -0
  197. package/dist/prompt-templates/builtins/index.js +5 -0
  198. package/dist/prompt-templates/builtins/planner.d.ts +4 -0
  199. package/dist/prompt-templates/builtins/planner.js +77 -0
  200. package/dist/prompt-templates/store.d.ts +45 -0
  201. package/dist/prompt-templates/store.js +224 -0
  202. package/dist/prompt-templates/types.d.ts +10 -0
  203. package/dist/prompt-templates/types.js +1 -0
  204. package/dist/scheduler/connected-channels.d.ts +24 -0
  205. package/dist/scheduler/connected-channels.js +57 -0
  206. package/dist/scheduler/scheduler.d.ts +22 -0
  207. package/dist/scheduler/scheduler.js +132 -0
  208. package/dist/scheduler/store.d.ts +27 -0
  209. package/dist/scheduler/store.js +205 -0
  210. package/dist/scheduler/types.d.ts +29 -0
  211. package/dist/scheduler/types.js +1 -0
  212. package/dist/security/command-validator.d.ts +22 -0
  213. package/dist/security/command-validator.js +160 -0
  214. package/dist/security/docker-sandbox.d.ts +48 -0
  215. package/dist/security/docker-sandbox.js +218 -0
  216. package/dist/security/env-filter.d.ts +8 -0
  217. package/dist/security/env-filter.js +41 -0
  218. package/dist/skills/loader.d.ts +33 -0
  219. package/dist/skills/loader.js +132 -0
  220. package/dist/skills/prompt.d.ts +6 -0
  221. package/dist/skills/prompt.js +17 -0
  222. package/dist/skills/read-tool.d.ts +7 -0
  223. package/dist/skills/read-tool.js +24 -0
  224. package/dist/skills/scanner.d.ts +6 -0
  225. package/dist/skills/scanner.js +73 -0
  226. package/dist/skills/types.d.ts +15 -0
  227. package/dist/skills/types.js +1 -0
  228. package/dist/tasks/inline-attachment-content.d.ts +9 -0
  229. package/dist/tasks/inline-attachment-content.js +64 -0
  230. package/dist/tasks/store.d.ts +112 -0
  231. package/dist/tasks/store.js +519 -0
  232. package/dist/tasks/types.d.ts +129 -0
  233. package/dist/tasks/types.js +80 -0
  234. package/dist/teams/status-config.d.ts +8 -0
  235. package/dist/teams/status-config.js +40 -0
  236. package/dist/teams/store.d.ts +111 -0
  237. package/dist/teams/store.js +671 -0
  238. package/dist/teams/types.d.ts +30 -0
  239. package/dist/teams/types.js +1 -0
  240. package/dist/tools/bash.d.ts +18 -0
  241. package/dist/tools/bash.js +64 -0
  242. package/dist/tools/channel-history.d.ts +10 -0
  243. package/dist/tools/channel-history.js +43 -0
  244. package/dist/tools/delegate.d.ts +20 -0
  245. package/dist/tools/delegate.js +299 -0
  246. package/dist/tools/fs.d.ts +4 -0
  247. package/dist/tools/fs.js +335 -0
  248. package/dist/tools/integration-toggle.d.ts +14 -0
  249. package/dist/tools/integration-toggle.js +47 -0
  250. package/dist/tools/memory.d.ts +13 -0
  251. package/dist/tools/memory.js +59 -0
  252. package/dist/tools/prompt-templates.d.ts +7 -0
  253. package/dist/tools/prompt-templates.js +133 -0
  254. package/dist/tools/registry.d.ts +6 -0
  255. package/dist/tools/registry.js +9 -0
  256. package/dist/tools/schedule.d.ts +8 -0
  257. package/dist/tools/schedule.js +219 -0
  258. package/dist/tools/speak.d.ts +10 -0
  259. package/dist/tools/speak.js +56 -0
  260. package/dist/tools/tasks.d.ts +67 -0
  261. package/dist/tools/tasks.js +288 -0
  262. package/dist/tools/teams.d.ts +22 -0
  263. package/dist/tools/teams.js +470 -0
  264. package/dist/tools/web-fetch.d.ts +3 -0
  265. package/dist/tools/web-fetch.js +22 -0
  266. package/dist/tts/edge.d.ts +10 -0
  267. package/dist/tts/edge.js +60 -0
  268. package/dist/tts/speak.d.ts +12 -0
  269. package/dist/tts/speak.js +81 -0
  270. package/dist/tts/transcribe.d.ts +5 -0
  271. package/dist/tts/transcribe.js +40 -0
  272. package/dist/utils.d.ts +5 -0
  273. package/dist/utils.js +22 -0
  274. package/dist/version.d.ts +1 -0
  275. package/dist/version.js +13 -0
  276. package/package.json +102 -0
  277. package/verybot.js +2 -0
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Table rendering for Markdown IR.
3
+ * Extracted from ir.ts to keep modules under ~500 LOC.
4
+ */
5
+ import type { MarkdownStyle, MarkdownStyleSpan, MarkdownLinkSpan } from "./ir.js";
6
+ /** Minimal target interface for appending rendered table output. */
7
+ export type TableTarget = {
8
+ text: string;
9
+ styles: MarkdownStyleSpan[];
10
+ links: MarkdownLinkSpan[];
11
+ };
12
+ type OpenStyle = {
13
+ style: MarkdownStyle;
14
+ start: number;
15
+ };
16
+ type LinkState = {
17
+ href: string;
18
+ labelStart: number;
19
+ };
20
+ /** A render target with open style tracking (used during cell parsing). */
21
+ export type CellRenderTarget = TableTarget & {
22
+ openStyles: OpenStyle[];
23
+ linkStack: LinkState[];
24
+ };
25
+ export type TableCell = {
26
+ text: string;
27
+ styles: MarkdownStyleSpan[];
28
+ links: MarkdownLinkSpan[];
29
+ };
30
+ export type TableState = {
31
+ headers: TableCell[];
32
+ rows: TableCell[][];
33
+ currentRow: TableCell[];
34
+ currentCell: CellRenderTarget | null;
35
+ inHeader: boolean;
36
+ };
37
+ export declare function initTableState(): TableState;
38
+ export declare function initCellTarget(): CellRenderTarget;
39
+ export declare function finishTableCell(cell: CellRenderTarget): TableCell;
40
+ export declare function trimCell(cell: TableCell): TableCell;
41
+ export declare function renderTableAsBullets(target: TableTarget, table: TableState): void;
42
+ export declare function renderTableAsCode(target: TableTarget, table: TableState, inList: boolean): void;
43
+ export {};
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Table rendering for Markdown IR.
3
+ * Extracted from ir.ts to keep modules under ~500 LOC.
4
+ */
5
+ export function initTableState() {
6
+ return {
7
+ headers: [],
8
+ rows: [],
9
+ currentRow: [],
10
+ currentCell: null,
11
+ inHeader: false,
12
+ };
13
+ }
14
+ export function initCellTarget() {
15
+ return {
16
+ text: "",
17
+ styles: [],
18
+ openStyles: [],
19
+ links: [],
20
+ linkStack: [],
21
+ };
22
+ }
23
+ function closeRemainingStyles(target) {
24
+ for (let i = target.openStyles.length - 1; i >= 0; i -= 1) {
25
+ const open = target.openStyles[i];
26
+ const end = target.text.length;
27
+ if (end > open.start) {
28
+ target.styles.push({
29
+ start: open.start,
30
+ end,
31
+ style: open.style,
32
+ });
33
+ }
34
+ }
35
+ target.openStyles = [];
36
+ }
37
+ export function finishTableCell(cell) {
38
+ closeRemainingStyles(cell);
39
+ return {
40
+ text: cell.text,
41
+ styles: cell.styles,
42
+ links: cell.links,
43
+ };
44
+ }
45
+ export function trimCell(cell) {
46
+ const text = cell.text;
47
+ let start = 0;
48
+ let end = text.length;
49
+ while (start < end && /\s/.test(text[start] ?? "")) {
50
+ start += 1;
51
+ }
52
+ while (end > start && /\s/.test(text[end - 1] ?? "")) {
53
+ end -= 1;
54
+ }
55
+ if (start === 0 && end === text.length) {
56
+ return cell;
57
+ }
58
+ const trimmedText = text.slice(start, end);
59
+ const trimmedLength = trimmedText.length;
60
+ const trimmedStyles = [];
61
+ for (const span of cell.styles) {
62
+ const sliceStart = Math.max(0, span.start - start);
63
+ const sliceEnd = Math.min(trimmedLength, span.end - start);
64
+ if (sliceEnd > sliceStart) {
65
+ trimmedStyles.push({ start: sliceStart, end: sliceEnd, style: span.style });
66
+ }
67
+ }
68
+ const trimmedLinks = [];
69
+ for (const span of cell.links) {
70
+ const sliceStart = Math.max(0, span.start - start);
71
+ const sliceEnd = Math.min(trimmedLength, span.end - start);
72
+ if (sliceEnd > sliceStart) {
73
+ trimmedLinks.push({ start: sliceStart, end: sliceEnd, href: span.href });
74
+ }
75
+ }
76
+ return { text: trimmedText, styles: trimmedStyles, links: trimmedLinks };
77
+ }
78
+ function appendCell(target, cell) {
79
+ if (!cell.text) {
80
+ return;
81
+ }
82
+ const start = target.text.length;
83
+ target.text += cell.text;
84
+ for (const span of cell.styles) {
85
+ target.styles.push({
86
+ start: start + span.start,
87
+ end: start + span.end,
88
+ style: span.style,
89
+ });
90
+ }
91
+ for (const link of cell.links) {
92
+ target.links.push({
93
+ start: start + link.start,
94
+ end: start + link.end,
95
+ href: link.href,
96
+ });
97
+ }
98
+ }
99
+ export function renderTableAsBullets(target, table) {
100
+ const headers = table.headers.map(trimCell);
101
+ const rows = table.rows.map((row) => row.map(trimCell));
102
+ if (headers.length === 0 && rows.length === 0) {
103
+ return;
104
+ }
105
+ const useFirstColAsLabel = headers.length > 1 && rows.length > 0;
106
+ if (useFirstColAsLabel) {
107
+ for (const row of rows) {
108
+ if (row.length === 0) {
109
+ continue;
110
+ }
111
+ const rowLabel = row[0];
112
+ if (rowLabel?.text) {
113
+ const labelStart = target.text.length;
114
+ appendCell(target, rowLabel);
115
+ const labelEnd = target.text.length;
116
+ if (labelEnd > labelStart) {
117
+ target.styles.push({ start: labelStart, end: labelEnd, style: "bold" });
118
+ }
119
+ target.text += "\n";
120
+ }
121
+ for (let i = 1; i < row.length; i++) {
122
+ const header = headers[i];
123
+ const value = row[i];
124
+ if (!value?.text) {
125
+ continue;
126
+ }
127
+ target.text += "\u2022 ";
128
+ if (header?.text) {
129
+ appendCell(target, header);
130
+ target.text += ": ";
131
+ }
132
+ else {
133
+ target.text += `Column ${i}: `;
134
+ }
135
+ appendCell(target, value);
136
+ target.text += "\n";
137
+ }
138
+ target.text += "\n";
139
+ }
140
+ }
141
+ else {
142
+ for (const row of rows) {
143
+ for (let i = 0; i < row.length; i++) {
144
+ const header = headers[i];
145
+ const value = row[i];
146
+ if (!value?.text) {
147
+ continue;
148
+ }
149
+ target.text += "\u2022 ";
150
+ if (header?.text) {
151
+ appendCell(target, header);
152
+ target.text += ": ";
153
+ }
154
+ appendCell(target, value);
155
+ target.text += "\n";
156
+ }
157
+ target.text += "\n";
158
+ }
159
+ }
160
+ }
161
+ export function renderTableAsCode(target, table, inList) {
162
+ const headers = table.headers.map(trimCell);
163
+ const rows = table.rows.map((row) => row.map(trimCell));
164
+ const columnCount = Math.max(headers.length, ...rows.map((row) => row.length));
165
+ if (columnCount === 0) {
166
+ return;
167
+ }
168
+ const widths = Array.from({ length: columnCount }, () => 0);
169
+ const updateWidths = (cells) => {
170
+ for (let i = 0; i < columnCount; i += 1) {
171
+ const cell = cells[i];
172
+ const width = cell?.text.length ?? 0;
173
+ if (widths[i] < width) {
174
+ widths[i] = width;
175
+ }
176
+ }
177
+ };
178
+ updateWidths(headers);
179
+ for (const row of rows) {
180
+ updateWidths(row);
181
+ }
182
+ const codeStart = target.text.length;
183
+ const appendRow = (cells) => {
184
+ target.text += "|";
185
+ for (let i = 0; i < columnCount; i += 1) {
186
+ target.text += " ";
187
+ const cell = cells[i];
188
+ if (cell) {
189
+ appendCell(target, cell);
190
+ }
191
+ const pad = widths[i] - (cell?.text.length ?? 0);
192
+ if (pad > 0) {
193
+ target.text += " ".repeat(pad);
194
+ }
195
+ target.text += " |";
196
+ }
197
+ target.text += "\n";
198
+ };
199
+ const appendDivider = () => {
200
+ target.text += "|";
201
+ for (let i = 0; i < columnCount; i += 1) {
202
+ const dashCount = Math.max(3, widths[i]);
203
+ target.text += ` ${"-".repeat(dashCount)} |`;
204
+ }
205
+ target.text += "\n";
206
+ };
207
+ appendRow(headers);
208
+ appendDivider();
209
+ for (const row of rows) {
210
+ appendRow(row);
211
+ }
212
+ const codeEnd = target.text.length;
213
+ if (codeEnd > codeStart) {
214
+ target.styles.push({ start: codeStart, end: codeEnd, style: "code_block" });
215
+ }
216
+ if (!inList) {
217
+ target.text += "\n";
218
+ }
219
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Table mode resolution for markdown rendering.
3
+ * Adapted from verybot markdown-tables.
4
+ */
5
+ import type { MarkdownTableMode } from "./ir.js";
6
+ /**
7
+ * Resolve the markdown table mode for a given channel.
8
+ *
9
+ * Resolution order:
10
+ * 1. Explicit per-channel config (`channelTableMode`)
11
+ * 2. Channel-specific default (signal/whatsapp → "bullets")
12
+ * 3. Global default ("code")
13
+ */
14
+ export declare function resolveMarkdownTableMode(params: {
15
+ channel: string;
16
+ channelTableMode?: MarkdownTableMode | string | null;
17
+ }): MarkdownTableMode;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Table mode resolution for markdown rendering.
3
+ * Adapted from verybot markdown-tables.
4
+ */
5
+ /** Per-channel defaults. */
6
+ const DEFAULT_TABLE_MODES = new Map([
7
+ ["signal", "bullets"],
8
+ ["whatsapp", "bullets"],
9
+ ]);
10
+ /** Fallback for channels not in the default map. */
11
+ const GLOBAL_DEFAULT = "code";
12
+ const isMarkdownTableMode = (value) => value === "off" || value === "bullets" || value === "code";
13
+ /**
14
+ * Resolve the markdown table mode for a given channel.
15
+ *
16
+ * Resolution order:
17
+ * 1. Explicit per-channel config (`channelTableMode`)
18
+ * 2. Channel-specific default (signal/whatsapp → "bullets")
19
+ * 3. Global default ("code")
20
+ */
21
+ export function resolveMarkdownTableMode(params) {
22
+ const { channel, channelTableMode } = params;
23
+ if (isMarkdownTableMode(channelTableMode)) {
24
+ return channelTableMode;
25
+ }
26
+ return DEFAULT_TABLE_MODES.get(channel) ?? GLOBAL_DEFAULT;
27
+ }
@@ -0,0 +1,16 @@
1
+ export interface EmbeddingProvider {
2
+ id: string;
3
+ model: string;
4
+ /** Returns embedding vector, or undefined if model is not ready yet. */
5
+ embed(text: string): Promise<number[] | undefined>;
6
+ embedBatch(texts: string[]): Promise<(number[] | undefined)[]>;
7
+ /** True once the model is downloaded and loaded. */
8
+ ready: boolean;
9
+ }
10
+ /**
11
+ * Create a local embedding provider using node-llama-cpp.
12
+ * The model is downloaded and loaded in the background — embed() returns
13
+ * undefined until ready, so the system falls back to FTS5-only search.
14
+ * Returns null if node-llama-cpp is not installed.
15
+ */
16
+ export declare function createEmbeddingProvider(provider: "local" | "none", modelPath?: string): Promise<EmbeddingProvider | null>;
@@ -0,0 +1,66 @@
1
+ import { logger } from "../logger.js";
2
+ const DEFAULT_MODEL = "hf:ggml-org/embeddinggemma-300M-GGUF/embeddinggemma-300M-Q8_0.gguf";
3
+ function normalizeEmbedding(vec) {
4
+ const sanitized = vec.map((v) => (Number.isFinite(v) ? v : 0));
5
+ const magnitude = Math.sqrt(sanitized.reduce((sum, v) => sum + v * v, 0));
6
+ if (magnitude < 1e-10)
7
+ return sanitized;
8
+ return sanitized.map((v) => v / magnitude);
9
+ }
10
+ /**
11
+ * Create a local embedding provider using node-llama-cpp.
12
+ * The model is downloaded and loaded in the background — embed() returns
13
+ * undefined until ready, so the system falls back to FTS5-only search.
14
+ * Returns null if node-llama-cpp is not installed.
15
+ */
16
+ export async function createEmbeddingProvider(provider, modelPath) {
17
+ if (provider === "none")
18
+ return null;
19
+ let nodeLlamaCpp;
20
+ try {
21
+ nodeLlamaCpp = await import("node-llama-cpp");
22
+ }
23
+ catch {
24
+ logger.warn("node-llama-cpp not installed. Falling back to FTS5-only search.");
25
+ return null;
26
+ }
27
+ const { getLlama, resolveModelFile, LlamaLogLevel } = nodeLlamaCpp;
28
+ let ctx = null;
29
+ const resolvedModel = modelPath || DEFAULT_MODEL;
30
+ const ep = {
31
+ id: "local",
32
+ model: resolvedModel,
33
+ ready: false,
34
+ async embed(text) {
35
+ if (!ctx)
36
+ return undefined;
37
+ const result = await ctx.getEmbeddingFor(text);
38
+ return normalizeEmbedding(Array.from(result.vector));
39
+ },
40
+ async embedBatch(texts) {
41
+ if (!ctx)
42
+ return texts.map(() => undefined);
43
+ return Promise.all(texts.map(async (t) => {
44
+ const result = await ctx.getEmbeddingFor(t);
45
+ return normalizeEmbedding(Array.from(result.vector));
46
+ }));
47
+ },
48
+ };
49
+ // Background init — download model + load context, never blocks startup
50
+ (async () => {
51
+ try {
52
+ logger.info(`Embedding model loading in background (${resolvedModel})...`);
53
+ const llama = await getLlama({ logLevel: LlamaLogLevel.error });
54
+ const resolved = await resolveModelFile(resolvedModel);
55
+ const model = await llama.loadModel({ modelPath: resolved });
56
+ ctx = await model.createEmbeddingContext();
57
+ ep.ready = true;
58
+ logger.info("Embedding model ready.");
59
+ }
60
+ catch (err) {
61
+ const msg = err instanceof Error ? err.message : String(err);
62
+ logger.warn(`Embedding model failed to load: ${msg}. Using FTS5-only search.`);
63
+ }
64
+ })();
65
+ return ep;
66
+ }
@@ -0,0 +1,16 @@
1
+ import type { MemoryStore } from "./store.js";
2
+ import type { EmbeddingProvider } from "./embedding.js";
3
+ export declare const MAX_EXPLICIT_FACT_LENGTH = 2000;
4
+ export interface SaveExplicitMemoryInput {
5
+ fact: string;
6
+ source: string;
7
+ teamId?: string;
8
+ }
9
+ export interface SaveExplicitMemoryResult {
10
+ saved: boolean;
11
+ fact: string;
12
+ }
13
+ /**
14
+ * Save one user-provided fact to long-term memory with consistent validation.
15
+ */
16
+ export declare function saveExplicitMemory(store: MemoryStore, embeddingProvider: EmbeddingProvider | null, input: SaveExplicitMemoryInput): Promise<SaveExplicitMemoryResult>;
@@ -0,0 +1,29 @@
1
+ import { randomUUID } from "crypto";
2
+ export const MAX_EXPLICIT_FACT_LENGTH = 2000;
3
+ /**
4
+ * Save one user-provided fact to long-term memory with consistent validation.
5
+ */
6
+ export async function saveExplicitMemory(store, embeddingProvider, input) {
7
+ if (typeof input.fact !== "string") {
8
+ throw new Error("fact is required");
9
+ }
10
+ const fact = input.fact.trim();
11
+ if (fact.length === 0) {
12
+ throw new Error("fact cannot be empty");
13
+ }
14
+ if (fact.length > MAX_EXPLICIT_FACT_LENGTH) {
15
+ throw new Error(`fact exceeds maximum length of ${MAX_EXPLICIT_FACT_LENGTH}`);
16
+ }
17
+ const embedding = embeddingProvider
18
+ ? await embeddingProvider.embed(fact) ?? undefined
19
+ : undefined;
20
+ const saved = store.save({
21
+ id: randomUUID(),
22
+ fact,
23
+ source: input.source,
24
+ timestamp: Date.now(),
25
+ teamId: input.teamId,
26
+ embedding,
27
+ });
28
+ return { saved, fact };
29
+ }
@@ -0,0 +1,13 @@
1
+ import { type LanguageModel, type ModelMessage } from "ai";
2
+ interface ExtractFactOptions {
3
+ /** If set, only return facts related to this topic. */
4
+ topic?: string;
5
+ /** Number of recent messages to inspect. */
6
+ maxMessages?: number;
7
+ }
8
+ /**
9
+ * Extract memorable facts from a conversation using an LLM.
10
+ * Returns an array of fact strings, or empty if nothing worth remembering.
11
+ */
12
+ export declare function extractFacts(model: LanguageModel, messages: ModelMessage[], options?: ExtractFactOptions): Promise<string[]>;
13
+ export {};
@@ -0,0 +1,82 @@
1
+ import { generateText } from "ai";
2
+ import { logger } from "../logger.js";
3
+ const MAX_RECENT_MESSAGES = 10;
4
+ function buildExtractionPrompt(topic) {
5
+ const topicSection = topic
6
+ ? `\nFOCUS MODE:
7
+ - Only include facts directly related to this topic: "${topic}".
8
+ - If no relevant facts are found for this topic, return [].
9
+ `
10
+ : "";
11
+ return `You extract memorable facts from conversations.
12
+
13
+ Review the conversation and identify facts worth remembering long-term about the user.${topicSection}
14
+
15
+ INCLUDE:
16
+ - Personal preferences (likes, dislikes, habits)
17
+ - Important context (name, job, location, family, timezone)
18
+ - Goals, projects, and recurring topics
19
+ - Communication preferences
20
+
21
+ EXCLUDE:
22
+ - Transient requests ("search for X", "open this URL")
23
+ - Generic small talk or greetings
24
+ - Tool usage details or technical artifacts
25
+ - Information the user explicitly asked to forget
26
+
27
+ Return ONLY a JSON array of short fact strings. Each fact should be a single sentence.
28
+ If there are no memorable facts, return an empty array: []
29
+
30
+ Example output:
31
+ ["User's name is Alice", "User prefers dark mode", "User works as a backend engineer"]`;
32
+ }
33
+ /**
34
+ * Extract memorable facts from a conversation using an LLM.
35
+ * Returns an array of fact strings, or empty if nothing worth remembering.
36
+ */
37
+ export async function extractFacts(model, messages, options = {}) {
38
+ // Only look at recent messages to avoid redundant extraction
39
+ const maxMessages = options.maxMessages ?? MAX_RECENT_MESSAGES;
40
+ const recent = messages.slice(-maxMessages);
41
+ const formatted = recent
42
+ .map((m) => {
43
+ if (m.role === "user") {
44
+ const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
45
+ return `User: ${content}`;
46
+ }
47
+ if (m.role === "assistant") {
48
+ const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
49
+ return `Assistant: ${content}`;
50
+ }
51
+ return null;
52
+ })
53
+ .filter(Boolean)
54
+ .join("\n");
55
+ if (!formatted.trim())
56
+ return [];
57
+ try {
58
+ const { text } = await generateText({
59
+ model,
60
+ system: buildExtractionPrompt(options.topic),
61
+ prompt: formatted,
62
+ });
63
+ // Parse JSON array from response (handle markdown code blocks and surrounding prose)
64
+ const cleaned = text.replace(/```json?\n?/g, "").replace(/```/g, "").trim();
65
+ // Try to find a JSON array in the response — the model sometimes wraps it in prose
66
+ const arrayMatch = cleaned.match(/\[[\s\S]*\]/);
67
+ if (!arrayMatch) {
68
+ // No array found — model returned plain text, nothing to extract
69
+ return [];
70
+ }
71
+ const parsed = JSON.parse(arrayMatch[0]);
72
+ if (!Array.isArray(parsed)) {
73
+ logger.warn("Fact extraction returned non-array");
74
+ return [];
75
+ }
76
+ return parsed.filter((f) => typeof f === "string" && f.trim().length > 0);
77
+ }
78
+ catch (err) {
79
+ logger.warn(`Fact extraction failed: ${err instanceof Error ? err.message : err}`);
80
+ return [];
81
+ }
82
+ }
@@ -0,0 +1,15 @@
1
+ import type { MemoryStore } from "./store.js";
2
+ import type { EmbeddingProvider } from "./embedding.js";
3
+ export interface SearchOptions {
4
+ limit?: number;
5
+ embeddingProvider?: EmbeddingProvider | null;
6
+ /** Restrict results to memories from this session source. */
7
+ source?: string;
8
+ /** When set, returns only team-specific memories. */
9
+ teamId?: string;
10
+ }
11
+ /**
12
+ * Hybrid memory search: runs FTS5 keyword search and (optionally) vector
13
+ * similarity search in parallel, then merges and deduplicates results.
14
+ */
15
+ export declare function searchMemory(store: MemoryStore, query: string, options?: SearchOptions): Promise<string[]>;
@@ -0,0 +1,57 @@
1
+ import { logger } from "../logger.js";
2
+ const VECTOR_WEIGHT = 0.7;
3
+ const TEXT_WEIGHT = 0.3;
4
+ /**
5
+ * Hybrid memory search: runs FTS5 keyword search and (optionally) vector
6
+ * similarity search in parallel, then merges and deduplicates results.
7
+ */
8
+ export async function searchMemory(store, query, options = {}) {
9
+ const limit = options.limit ?? 5;
10
+ try {
11
+ const { source, teamId } = options;
12
+ // FTS5 keyword search (always available)
13
+ const textResults = store.searchByText(query, limit, source, teamId);
14
+ // Vector search (only if embeddings available)
15
+ let vectorResults = [];
16
+ if (options.embeddingProvider && store.hasVectorSearch) {
17
+ try {
18
+ const embedding = await options.embeddingProvider.embed(query);
19
+ if (embedding) {
20
+ vectorResults = store.searchByVector(embedding, limit, source, teamId);
21
+ }
22
+ }
23
+ catch (err) {
24
+ logger.warn(`Vector search failed: ${err instanceof Error ? err.message : err}`);
25
+ }
26
+ }
27
+ // Merge and deduplicate
28
+ const scored = new Map();
29
+ // Score vector results (higher rank = higher score, inversely proportional to index)
30
+ for (let i = 0; i < vectorResults.length; i++) {
31
+ const r = vectorResults[i];
32
+ const vectorScore = 1 - i / vectorResults.length; // 1.0 → ~0.0
33
+ scored.set(r.id, { fact: r.fact, score: VECTOR_WEIGHT * vectorScore });
34
+ }
35
+ // Score text results and merge
36
+ for (let i = 0; i < textResults.length; i++) {
37
+ const r = textResults[i];
38
+ const textScore = 1 - i / textResults.length;
39
+ const existing = scored.get(r.id);
40
+ if (existing) {
41
+ existing.score += TEXT_WEIGHT * textScore;
42
+ }
43
+ else {
44
+ scored.set(r.id, { fact: r.fact, score: TEXT_WEIGHT * textScore });
45
+ }
46
+ }
47
+ // Sort by combined score, return top N facts
48
+ return Array.from(scored.values())
49
+ .sort((a, b) => b.score - a.score)
50
+ .slice(0, limit)
51
+ .map((r) => r.fact);
52
+ }
53
+ catch (err) {
54
+ logger.error(`Memory search error: ${err instanceof Error ? err.message : err}`);
55
+ return [];
56
+ }
57
+ }
@@ -0,0 +1,23 @@
1
+ import type { LanguageModel, ModelMessage } from "ai";
2
+ import type { MemoryStore } from "./store.js";
3
+ import type { EmbeddingProvider } from "./embedding.js";
4
+ export interface LearnSessionMemoriesInput {
5
+ model: LanguageModel;
6
+ memoryStore: MemoryStore;
7
+ embeddingProvider: EmbeddingProvider | null;
8
+ sessionKey: string;
9
+ messages: ModelMessage[];
10
+ teamId?: string;
11
+ topic?: string;
12
+ }
13
+ export interface LearnSessionMemoriesResult {
14
+ topic?: string;
15
+ extracted: number;
16
+ saved: number;
17
+ skipped: number;
18
+ savedFacts: string[];
19
+ }
20
+ /**
21
+ * Extract and save learnable facts from session messages.
22
+ */
23
+ export declare function learnSessionMemories(input: LearnSessionMemoriesInput): Promise<LearnSessionMemoriesResult>;
@@ -0,0 +1,55 @@
1
+ import { extractFacts } from "./extractor.js";
2
+ import { saveExplicitMemory } from "./explicit.js";
3
+ const MAX_LEARN_SOURCE_MESSAGES = 40;
4
+ /**
5
+ * Extract and save learnable facts from session messages.
6
+ */
7
+ export async function learnSessionMemories(input) {
8
+ const topic = normalizeTopic(input.topic);
9
+ const extractedFacts = await extractFacts(input.model, input.messages, {
10
+ topic,
11
+ maxMessages: MAX_LEARN_SOURCE_MESSAGES,
12
+ });
13
+ if (extractedFacts.length === 0) {
14
+ return {
15
+ topic,
16
+ extracted: 0,
17
+ saved: 0,
18
+ skipped: 0,
19
+ savedFacts: [],
20
+ };
21
+ }
22
+ const savedFacts = [];
23
+ let skipped = 0;
24
+ for (const fact of extractedFacts) {
25
+ try {
26
+ const result = await saveExplicitMemory(input.memoryStore, input.embeddingProvider, {
27
+ fact,
28
+ source: input.sessionKey,
29
+ teamId: input.teamId,
30
+ });
31
+ if (result.saved) {
32
+ savedFacts.push(result.fact);
33
+ }
34
+ else {
35
+ skipped++;
36
+ }
37
+ }
38
+ catch {
39
+ skipped++;
40
+ }
41
+ }
42
+ return {
43
+ topic,
44
+ extracted: extractedFacts.length,
45
+ saved: savedFacts.length,
46
+ skipped,
47
+ savedFacts,
48
+ };
49
+ }
50
+ function normalizeTopic(topic) {
51
+ if (typeof topic !== "string")
52
+ return undefined;
53
+ const trimmed = topic.trim();
54
+ return trimmed.length > 0 ? trimmed : undefined;
55
+ }