verybot 0.1.3
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.
Potentially problematic release.
This version of verybot might be problematic. Click here for more details.
- package/dist/brain/agent-registry.d.ts +75 -0
- package/dist/brain/agent-registry.js +124 -0
- package/dist/brain/agent.d.ts +146 -0
- package/dist/brain/agent.js +680 -0
- package/dist/brain/channel-store.d.ts +27 -0
- package/dist/brain/channel-store.js +78 -0
- package/dist/brain/compaction.d.ts +37 -0
- package/dist/brain/compaction.js +214 -0
- package/dist/brain/context.d.ts +33 -0
- package/dist/brain/context.js +77 -0
- package/dist/brain/delegation-store.d.ts +33 -0
- package/dist/brain/delegation-store.js +106 -0
- package/dist/brain/loop.d.ts +21 -0
- package/dist/brain/loop.js +161 -0
- package/dist/brain/mcp-adapter.d.ts +39 -0
- package/dist/brain/mcp-adapter.js +227 -0
- package/dist/brain/memory-extractor.d.ts +26 -0
- package/dist/brain/memory-extractor.js +82 -0
- package/dist/brain/providers.d.ts +10 -0
- package/dist/brain/providers.js +69 -0
- package/dist/brain/queue.d.ts +18 -0
- package/dist/brain/queue.js +84 -0
- package/dist/brain/run-tools.d.ts +47 -0
- package/dist/brain/run-tools.js +84 -0
- package/dist/brain/session-key.d.ts +23 -0
- package/dist/brain/session-key.js +41 -0
- package/dist/brain/session-state.d.ts +36 -0
- package/dist/brain/session-state.js +51 -0
- package/dist/brain/session-store.d.ts +50 -0
- package/dist/brain/session-store.js +207 -0
- package/dist/brain/session.d.ts +32 -0
- package/dist/brain/session.js +75 -0
- package/dist/brain/utils.d.ts +4 -0
- package/dist/brain/utils.js +26 -0
- package/dist/brain/worker-coordinator.d.ts +25 -0
- package/dist/brain/worker-coordinator.js +83 -0
- package/dist/channels/commands.d.ts +35 -0
- package/dist/channels/commands.js +65 -0
- package/dist/channels/discord/channel.d.ts +18 -0
- package/dist/channels/discord/channel.js +154 -0
- package/dist/channels/discord/markdown.d.ts +19 -0
- package/dist/channels/discord/markdown.js +62 -0
- package/dist/channels/manager.d.ts +29 -0
- package/dist/channels/manager.js +100 -0
- package/dist/channels/slack/channel.d.ts +26 -0
- package/dist/channels/slack/channel.js +207 -0
- package/dist/channels/slack/markdown.d.ts +19 -0
- package/dist/channels/slack/markdown.js +62 -0
- package/dist/channels/specs.d.ts +21 -0
- package/dist/channels/specs.js +96 -0
- package/dist/channels/telegram/channel.d.ts +18 -0
- package/dist/channels/telegram/channel.js +156 -0
- package/dist/channels/telegram/markdown.d.ts +17 -0
- package/dist/channels/telegram/markdown.js +66 -0
- package/dist/channels/types.d.ts +26 -0
- package/dist/channels/types.js +1 -0
- package/dist/channels/whatsapp/channel.d.ts +23 -0
- package/dist/channels/whatsapp/channel.js +242 -0
- package/dist/channels/whatsapp/markdown.d.ts +20 -0
- package/dist/channels/whatsapp/markdown.js +51 -0
- package/dist/cli/config.d.ts +5 -0
- package/dist/cli/config.js +78 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +13 -0
- package/dist/computer/browser/actions.d.ts +31 -0
- package/dist/computer/browser/actions.js +148 -0
- package/dist/computer/browser/manager.d.ts +55 -0
- package/dist/computer/browser/manager.js +496 -0
- package/dist/computer/browser/profile-badge.d.ts +13 -0
- package/dist/computer/browser/profile-badge.js +67 -0
- package/dist/computer/browser/screenshot.d.ts +5 -0
- package/dist/computer/browser/screenshot.js +21 -0
- package/dist/computer/browser/snapshot.d.ts +30 -0
- package/dist/computer/browser/snapshot.js +242 -0
- package/dist/computer/browser/tools.d.ts +5 -0
- package/dist/computer/browser/tools.js +167 -0
- package/dist/computer/desktop/adapter.d.ts +25 -0
- package/dist/computer/desktop/adapter.js +11 -0
- package/dist/computer/desktop/macos.d.ts +24 -0
- package/dist/computer/desktop/macos.js +223 -0
- package/dist/computer/desktop/tools.d.ts +25 -0
- package/dist/computer/desktop/tools.js +114 -0
- package/dist/config/agent-config.d.ts +41 -0
- package/dist/config/agent-config.js +14 -0
- package/dist/config/model-catalog.d.ts +22 -0
- package/dist/config/model-catalog.js +99 -0
- package/dist/config/store.d.ts +25 -0
- package/dist/config/store.js +143 -0
- package/dist/config.d.ts +103 -0
- package/dist/config.js +224 -0
- package/dist/control-ui/assets/index-BANXNUyt.js +143 -0
- package/dist/control-ui/assets/index-BSUFrP9R.css +1 -0
- package/dist/control-ui/assets/noto-sans-cyrillic-ext-wght-normal-DSNfmdVt.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-cyrillic-wght-normal-B2hlT84T.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-devanagari-wght-normal-Cv-Vwajv.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-greek-ext-wght-normal-12T8GTDR.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-greek-wght-normal-Ymb6dZNd.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-latin-ext-wght-normal-W1qJv59z.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-latin-wght-normal-BYSzYMf3.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-vietnamese-wght-normal-DLTJy58D.woff2 +0 -0
- package/dist/control-ui/index.html +14 -0
- package/dist/control-ui/vite.svg +1 -0
- package/dist/events.d.ts +2 -0
- package/dist/events.js +11 -0
- package/dist/gateway/broadcast.d.ts +5 -0
- package/dist/gateway/broadcast.js +33 -0
- package/dist/gateway/methods/chat.d.ts +24 -0
- package/dist/gateway/methods/chat.js +19 -0
- package/dist/gateway/methods/config.d.ts +13 -0
- package/dist/gateway/methods/config.js +14 -0
- package/dist/gateway/methods/models.d.ts +10 -0
- package/dist/gateway/methods/models.js +14 -0
- package/dist/gateway/methods/prompt-templates.d.ts +23 -0
- package/dist/gateway/methods/prompt-templates.js +82 -0
- package/dist/gateway/methods/scheduler.d.ts +62 -0
- package/dist/gateway/methods/scheduler.js +129 -0
- package/dist/gateway/methods/sessions.d.ts +26 -0
- package/dist/gateway/methods/sessions.js +54 -0
- package/dist/gateway/methods/skills.d.ts +35 -0
- package/dist/gateway/methods/skills.js +202 -0
- package/dist/gateway/methods/system.d.ts +12 -0
- package/dist/gateway/methods/system.js +39 -0
- package/dist/gateway/methods/tasks.d.ts +21 -0
- package/dist/gateway/methods/tasks.js +46 -0
- package/dist/gateway/methods/teams.d.ts +70 -0
- package/dist/gateway/methods/teams.js +374 -0
- package/dist/gateway/methods/tools.d.ts +6 -0
- package/dist/gateway/methods/tools.js +7 -0
- package/dist/gateway/methods/whatsapp.d.ts +19 -0
- package/dist/gateway/methods/whatsapp.js +35 -0
- package/dist/gateway/rpc.d.ts +38 -0
- package/dist/gateway/rpc.js +75 -0
- package/dist/gateway/server.d.ts +4 -0
- package/dist/gateway/server.js +133 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +212 -0
- package/dist/integrations/github.d.ts +7 -0
- package/dist/integrations/github.js +133 -0
- package/dist/integrations/mcp.d.ts +7 -0
- package/dist/integrations/mcp.js +106 -0
- package/dist/integrations/registry.d.ts +43 -0
- package/dist/integrations/registry.js +258 -0
- package/dist/integrations/scanner.d.ts +10 -0
- package/dist/integrations/scanner.js +122 -0
- package/dist/integrations/twitter.d.ts +10 -0
- package/dist/integrations/twitter.js +120 -0
- package/dist/integrations/types.d.ts +72 -0
- package/dist/integrations/types.js +1 -0
- package/dist/logger.d.ts +16 -0
- package/dist/logger.js +104 -0
- package/dist/markdown/chunk.d.ts +9 -0
- package/dist/markdown/chunk.js +52 -0
- package/dist/markdown/ir.d.ts +37 -0
- package/dist/markdown/ir.js +529 -0
- package/dist/markdown/render.d.ts +22 -0
- package/dist/markdown/render.js +148 -0
- package/dist/markdown/table-render.d.ts +43 -0
- package/dist/markdown/table-render.js +219 -0
- package/dist/markdown/tables.d.ts +17 -0
- package/dist/markdown/tables.js +27 -0
- package/dist/memory/embedding.d.ts +16 -0
- package/dist/memory/embedding.js +66 -0
- package/dist/memory/extractor.d.ts +6 -0
- package/dist/memory/extractor.js +72 -0
- package/dist/memory/search.d.ts +15 -0
- package/dist/memory/search.js +57 -0
- package/dist/memory/store.d.ts +34 -0
- package/dist/memory/store.js +328 -0
- package/dist/memory/types.d.ts +9 -0
- package/dist/memory/types.js +2 -0
- package/dist/paths.d.ts +20 -0
- package/dist/paths.js +29 -0
- package/dist/prompt-templates/builtins.d.ts +2 -0
- package/dist/prompt-templates/builtins.js +72 -0
- package/dist/prompt-templates/store.d.ts +39 -0
- package/dist/prompt-templates/store.js +174 -0
- package/dist/prompt-templates/types.d.ts +10 -0
- package/dist/prompt-templates/types.js +1 -0
- package/dist/scheduler/connected-channels.d.ts +24 -0
- package/dist/scheduler/connected-channels.js +57 -0
- package/dist/scheduler/scheduler.d.ts +22 -0
- package/dist/scheduler/scheduler.js +132 -0
- package/dist/scheduler/store.d.ts +27 -0
- package/dist/scheduler/store.js +205 -0
- package/dist/scheduler/types.d.ts +29 -0
- package/dist/scheduler/types.js +1 -0
- package/dist/security/command-validator.d.ts +22 -0
- package/dist/security/command-validator.js +160 -0
- package/dist/security/docker-sandbox.d.ts +48 -0
- package/dist/security/docker-sandbox.js +218 -0
- package/dist/security/env-filter.d.ts +8 -0
- package/dist/security/env-filter.js +41 -0
- package/dist/skills/loader.d.ts +33 -0
- package/dist/skills/loader.js +132 -0
- package/dist/skills/prompt.d.ts +6 -0
- package/dist/skills/prompt.js +17 -0
- package/dist/skills/read-tool.d.ts +7 -0
- package/dist/skills/read-tool.js +24 -0
- package/dist/skills/scanner.d.ts +6 -0
- package/dist/skills/scanner.js +73 -0
- package/dist/skills/types.d.ts +15 -0
- package/dist/skills/types.js +1 -0
- package/dist/tasks/store.d.ts +47 -0
- package/dist/tasks/store.js +193 -0
- package/dist/tasks/types.d.ts +75 -0
- package/dist/tasks/types.js +32 -0
- package/dist/teams/store.d.ts +78 -0
- package/dist/teams/store.js +420 -0
- package/dist/teams/types.d.ts +23 -0
- package/dist/teams/types.js +1 -0
- package/dist/tools/bash.d.ts +16 -0
- package/dist/tools/bash.js +62 -0
- package/dist/tools/channel-history.d.ts +10 -0
- package/dist/tools/channel-history.js +43 -0
- package/dist/tools/delegate.d.ts +16 -0
- package/dist/tools/delegate.js +216 -0
- package/dist/tools/fs.d.ts +4 -0
- package/dist/tools/fs.js +335 -0
- package/dist/tools/integration-toggle.d.ts +14 -0
- package/dist/tools/integration-toggle.js +47 -0
- package/dist/tools/memory.d.ts +13 -0
- package/dist/tools/memory.js +65 -0
- package/dist/tools/registry.d.ts +6 -0
- package/dist/tools/registry.js +9 -0
- package/dist/tools/schedule.d.ts +8 -0
- package/dist/tools/schedule.js +219 -0
- package/dist/tools/speak.d.ts +10 -0
- package/dist/tools/speak.js +56 -0
- package/dist/tools/tasks.d.ts +29 -0
- package/dist/tools/tasks.js +92 -0
- package/dist/tools/teams.d.ts +7 -0
- package/dist/tools/teams.js +180 -0
- package/dist/tools/web-fetch.d.ts +3 -0
- package/dist/tools/web-fetch.js +22 -0
- package/dist/tts/edge.d.ts +10 -0
- package/dist/tts/edge.js +60 -0
- package/dist/tts/speak.d.ts +12 -0
- package/dist/tts/speak.js +81 -0
- package/dist/tts/transcribe.d.ts +5 -0
- package/dist/tts/transcribe.js +40 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +22 -0
- package/package.json +90 -0
- 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,6 @@
|
|
|
1
|
+
import { type LanguageModel, type ModelMessage } from "ai";
|
|
2
|
+
/**
|
|
3
|
+
* Extract memorable facts from a conversation using an LLM.
|
|
4
|
+
* Returns an array of fact strings, or empty if nothing worth remembering.
|
|
5
|
+
*/
|
|
6
|
+
export declare function extractFacts(model: LanguageModel, messages: ModelMessage[]): Promise<string[]>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { generateText } from "ai";
|
|
2
|
+
import { logger } from "../logger.js";
|
|
3
|
+
const EXTRACTION_PROMPT = `You extract memorable facts from conversations.
|
|
4
|
+
|
|
5
|
+
Review the conversation and identify facts worth remembering long-term about the user.
|
|
6
|
+
|
|
7
|
+
INCLUDE:
|
|
8
|
+
- Personal preferences (likes, dislikes, habits)
|
|
9
|
+
- Important context (name, job, location, family, timezone)
|
|
10
|
+
- Goals, projects, and recurring topics
|
|
11
|
+
- Communication preferences
|
|
12
|
+
|
|
13
|
+
EXCLUDE:
|
|
14
|
+
- Transient requests ("search for X", "open this URL")
|
|
15
|
+
- Generic small talk or greetings
|
|
16
|
+
- Tool usage details or technical artifacts
|
|
17
|
+
- Information the user explicitly asked to forget
|
|
18
|
+
|
|
19
|
+
Return ONLY a JSON array of short fact strings. Each fact should be a single sentence.
|
|
20
|
+
If there are no memorable facts, return an empty array: []
|
|
21
|
+
|
|
22
|
+
Example output:
|
|
23
|
+
["User's name is Alice", "User prefers dark mode", "User works as a backend engineer"]`;
|
|
24
|
+
/**
|
|
25
|
+
* Extract memorable facts from a conversation using an LLM.
|
|
26
|
+
* Returns an array of fact strings, or empty if nothing worth remembering.
|
|
27
|
+
*/
|
|
28
|
+
export async function extractFacts(model, messages) {
|
|
29
|
+
// Only look at recent messages (last 10) to avoid redundant extraction
|
|
30
|
+
const recent = messages.slice(-10);
|
|
31
|
+
const formatted = recent
|
|
32
|
+
.map((m) => {
|
|
33
|
+
if (m.role === "user") {
|
|
34
|
+
const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
35
|
+
return `User: ${content}`;
|
|
36
|
+
}
|
|
37
|
+
if (m.role === "assistant") {
|
|
38
|
+
const content = typeof m.content === "string" ? m.content : JSON.stringify(m.content);
|
|
39
|
+
return `Assistant: ${content}`;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
})
|
|
43
|
+
.filter(Boolean)
|
|
44
|
+
.join("\n");
|
|
45
|
+
if (!formatted.trim())
|
|
46
|
+
return [];
|
|
47
|
+
try {
|
|
48
|
+
const { text } = await generateText({
|
|
49
|
+
model,
|
|
50
|
+
system: EXTRACTION_PROMPT,
|
|
51
|
+
prompt: formatted,
|
|
52
|
+
});
|
|
53
|
+
// Parse JSON array from response (handle markdown code blocks and surrounding prose)
|
|
54
|
+
const cleaned = text.replace(/```json?\n?/g, "").replace(/```/g, "").trim();
|
|
55
|
+
// Try to find a JSON array in the response — the model sometimes wraps it in prose
|
|
56
|
+
const arrayMatch = cleaned.match(/\[[\s\S]*\]/);
|
|
57
|
+
if (!arrayMatch) {
|
|
58
|
+
// No array found — model returned plain text, nothing to extract
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
const parsed = JSON.parse(arrayMatch[0]);
|
|
62
|
+
if (!Array.isArray(parsed)) {
|
|
63
|
+
logger.warn("Fact extraction returned non-array");
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
return parsed.filter((f) => typeof f === "string" && f.trim().length > 0);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
logger.warn(`Fact extraction failed: ${err instanceof Error ? err.message : err}`);
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -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,34 @@
|
|
|
1
|
+
import type { MemoryEntry } from "./types.js";
|
|
2
|
+
export declare class MemoryStore {
|
|
3
|
+
private db;
|
|
4
|
+
private vectorEnabled;
|
|
5
|
+
private vecTableCreated;
|
|
6
|
+
private constructor();
|
|
7
|
+
/** Async factory — loads sqlite-vec extension if available. */
|
|
8
|
+
static create(dbPath: string): Promise<MemoryStore>;
|
|
9
|
+
get hasVectorSearch(): boolean;
|
|
10
|
+
private createSchema;
|
|
11
|
+
/** Add team_id column if it doesn't exist yet (idempotent). */
|
|
12
|
+
private migrateTeamId;
|
|
13
|
+
private loadVecExtension;
|
|
14
|
+
private ensureVecTable;
|
|
15
|
+
/** Save a memory entry. Returns false if duplicate or near-duplicate fact exists. */
|
|
16
|
+
save(entry: MemoryEntry): boolean;
|
|
17
|
+
/** Full-text search using FTS5/BM25. Optionally scoped to a source and/or team. */
|
|
18
|
+
searchByText(query: string, limit?: number, source?: string, teamId?: string): MemoryEntry[];
|
|
19
|
+
/** Vector similarity search using sqlite-vec. Optionally scoped to a source and/or team. */
|
|
20
|
+
searchByVector(embedding: number[], limit?: number, source?: string, teamId?: string): MemoryEntry[];
|
|
21
|
+
/** Delete a single memory by its ID. Optionally verify team ownership. Returns true if found and deleted. */
|
|
22
|
+
deleteById(id: string, teamId?: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Paginated list of memories for a specific team (or global if teamId is null).
|
|
25
|
+
* Intended for CRUD management (listing/deleting).
|
|
26
|
+
*/
|
|
27
|
+
listByTeam(teamId: string | null, limit?: number, offset?: number): {
|
|
28
|
+
entries: MemoryEntry[];
|
|
29
|
+
total: number;
|
|
30
|
+
};
|
|
31
|
+
/** Delete all memories from a given session source. Returns count deleted. */
|
|
32
|
+
deleteBySource(source: string): number;
|
|
33
|
+
close(): void;
|
|
34
|
+
}
|