@qearlyao/familiar 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -14
- package/config.example.toml +1 -1
- package/dist/added-models.js +6 -15
- package/dist/agent-events.js +1 -3
- package/dist/agent.js +3 -4
- package/dist/browser-tools.js +15 -11
- package/dist/chat-log.js +3 -2
- package/dist/cli.js +2 -2
- package/dist/config-overrides.js +5 -14
- package/dist/config-registry.js +1 -4
- package/dist/config.js +45 -113
- package/dist/contact-note.js +2 -12
- package/dist/data-retention.js +1 -3
- package/dist/discord.js +72 -19
- package/dist/generated-media.js +3 -2
- package/dist/hot-reload.js +1 -3
- package/dist/image-gen.js +12 -51
- package/dist/inbound-attachments.js +64 -23
- package/dist/memory/diary/ambient-injector.js +1 -3
- package/dist/memory/diary/ambient.js +1 -3
- package/dist/memory/diary/chunks.js +1 -3
- package/dist/memory/diary/indexer.js +1 -3
- package/dist/memory/doctor.js +3 -8
- package/dist/memory/index/chunk-indexer.js +27 -6
- package/dist/memory/index/retrieval.js +1 -3
- package/dist/memory/index/store.js +47 -19
- package/dist/memory/lcm/backfill.js +19 -16
- package/dist/memory/lcm/context-transformer.js +17 -29
- package/dist/memory/lcm/context.js +10 -4
- package/dist/memory/lcm/eviction-score.js +25 -13
- package/dist/memory/lcm/indexer.js +1 -5
- package/dist/memory/lcm/normalize.js +22 -1
- package/dist/memory/lcm/store.js +27 -24
- package/dist/memory/operator.js +3 -31
- package/dist/memory/service.js +1 -3
- package/dist/memory/tools.js +0 -4
- package/dist/memory/util.js +6 -0
- package/dist/models.js +3 -0
- package/dist/persona.js +3 -15
- package/dist/runtime.js +12 -23
- package/dist/scheduler.js +15 -49
- package/dist/service.js +39 -27
- package/dist/settings.js +7 -32
- package/dist/silent-marker.js +64 -0
- package/dist/tts.js +0 -6
- package/dist/util/fs.js +41 -0
- package/dist/util/guards.js +8 -0
- package/dist/util/image-mime.js +31 -0
- package/dist/util/time.js +29 -0
- package/dist/web-auth.js +4 -1
- package/dist/web-static.js +36 -1
- package/dist/web-tools.js +8 -5
- package/dist/web.js +253 -69
- package/npm-shrinkwrap.json +5139 -0
- package/package.json +5 -4
- package/web/dist/assets/index-B23WT77N.js +63 -0
- package/web/dist/assets/index-D3MotFzN.css +2 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-BPZQbZh5.js +0 -61
- package/web/dist/assets/index-CcQ13VAY.css +0 -2
package/dist/tts.js
CHANGED
|
@@ -129,13 +129,7 @@ export function createTtsTool(config, mediaSink) {
|
|
|
129
129
|
return {
|
|
130
130
|
content: [{ type: "text", text: formatTtsNotice(name) }],
|
|
131
131
|
details: {
|
|
132
|
-
provider: "elevenlabs",
|
|
133
|
-
voiceId,
|
|
134
|
-
modelId: config.tts.modelId,
|
|
135
|
-
outputFormat,
|
|
136
132
|
localPath,
|
|
137
|
-
mimeType,
|
|
138
|
-
size: buffer.length,
|
|
139
133
|
},
|
|
140
134
|
};
|
|
141
135
|
},
|
package/dist/util/fs.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { mkdir, open, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
export function isEnoent(error) {
|
|
4
|
+
return !!error && typeof error === "object" && "code" in error && error.code === "ENOENT";
|
|
5
|
+
}
|
|
6
|
+
export async function readFileOrNull(path, encoding) {
|
|
7
|
+
try {
|
|
8
|
+
return await readFile(path, encoding);
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
if (isEnoent(error))
|
|
12
|
+
return null;
|
|
13
|
+
throw error;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
async function fsyncFile(path) {
|
|
17
|
+
const handle = await open(path, "r");
|
|
18
|
+
try {
|
|
19
|
+
await handle.sync();
|
|
20
|
+
}
|
|
21
|
+
finally {
|
|
22
|
+
await handle.close();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export async function atomicWriteJson(path, value) {
|
|
26
|
+
await mkdir(dirname(path), { recursive: true });
|
|
27
|
+
const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
28
|
+
await writeFile(tmpPath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
29
|
+
await fsyncFile(tmpPath);
|
|
30
|
+
await rename(tmpPath, path);
|
|
31
|
+
}
|
|
32
|
+
export function createWriteQueue(logLabel) {
|
|
33
|
+
let queue = Promise.resolve();
|
|
34
|
+
return async (write) => {
|
|
35
|
+
const run = queue.then(write, write);
|
|
36
|
+
queue = run.then(() => undefined, (error) => {
|
|
37
|
+
console.error(`${logLabel} write failed`, error);
|
|
38
|
+
});
|
|
39
|
+
return run;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function isRecord(value) {
|
|
2
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
3
|
+
}
|
|
4
|
+
export function readEnum(value, path, allowed) {
|
|
5
|
+
if (typeof value === "string" && allowed.includes(value))
|
|
6
|
+
return value;
|
|
7
|
+
throw new Error(`Config value ${path} must be one of ${allowed.map((item) => JSON.stringify(item)).join(", ")}`);
|
|
8
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { extname } from "node:path";
|
|
2
|
+
export const IMAGE_MIME_BY_EXTENSION = {
|
|
3
|
+
".jpg": "image/jpeg",
|
|
4
|
+
".jpeg": "image/jpeg",
|
|
5
|
+
".png": "image/png",
|
|
6
|
+
".gif": "image/gif",
|
|
7
|
+
".webp": "image/webp",
|
|
8
|
+
};
|
|
9
|
+
export const IMAGE_EXTENSION_BY_MIME = {
|
|
10
|
+
"image/jpeg": ".jpg",
|
|
11
|
+
"image/png": ".png",
|
|
12
|
+
"image/gif": ".gif",
|
|
13
|
+
"image/webp": ".webp",
|
|
14
|
+
};
|
|
15
|
+
export function imageMimeTypeFromPath(path) {
|
|
16
|
+
return IMAGE_MIME_BY_EXTENSION[extname(path).toLowerCase()];
|
|
17
|
+
}
|
|
18
|
+
export function sniffImageMimeType(buffer) {
|
|
19
|
+
if (buffer.subarray(0, 3).equals(Buffer.from([0xff, 0xd8, 0xff])))
|
|
20
|
+
return "image/jpeg";
|
|
21
|
+
if (buffer.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) {
|
|
22
|
+
return "image/png";
|
|
23
|
+
}
|
|
24
|
+
if (buffer.subarray(0, 6).toString("ascii") === "GIF87a" || buffer.subarray(0, 6).toString("ascii") === "GIF89a") {
|
|
25
|
+
return "image/gif";
|
|
26
|
+
}
|
|
27
|
+
if (buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
28
|
+
return "image/webp";
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function toDate(value) {
|
|
2
|
+
if (value instanceof Date)
|
|
3
|
+
return value;
|
|
4
|
+
return new Date(value);
|
|
5
|
+
}
|
|
6
|
+
function formatOffset(date) {
|
|
7
|
+
const offsetMinutes = -date.getTimezoneOffset();
|
|
8
|
+
const sign = offsetMinutes >= 0 ? "+" : "-";
|
|
9
|
+
const absolute = Math.abs(offsetMinutes);
|
|
10
|
+
const hours = Math.floor(absolute / 60);
|
|
11
|
+
const minutes = absolute % 60;
|
|
12
|
+
return minutes === 0 ? `GMT${sign}${hours}` : `GMT${sign}${hours}:${String(minutes).padStart(2, "0")}`;
|
|
13
|
+
}
|
|
14
|
+
export function formatLocalTimestamp(value) {
|
|
15
|
+
const date = toDate(value);
|
|
16
|
+
if (Number.isNaN(date.getTime()))
|
|
17
|
+
return String(value);
|
|
18
|
+
const localDate = [
|
|
19
|
+
date.getFullYear(),
|
|
20
|
+
String(date.getMonth() + 1).padStart(2, "0"),
|
|
21
|
+
String(date.getDate()).padStart(2, "0"),
|
|
22
|
+
].join("-");
|
|
23
|
+
const localTime = [
|
|
24
|
+
String(date.getHours()).padStart(2, "0"),
|
|
25
|
+
String(date.getMinutes()).padStart(2, "0"),
|
|
26
|
+
String(date.getSeconds()).padStart(2, "0"),
|
|
27
|
+
].join(":");
|
|
28
|
+
return `${localDate} ${localTime} ${formatOffset(date)}`;
|
|
29
|
+
}
|
package/dist/web-auth.js
CHANGED
|
@@ -11,7 +11,10 @@ function parseCookies(header) {
|
|
|
11
11
|
const [name, ...valueParts] = part.trim().split("=");
|
|
12
12
|
if (!name)
|
|
13
13
|
continue;
|
|
14
|
-
|
|
14
|
+
try {
|
|
15
|
+
cookies[name] = decodeURIComponent(valueParts.join("="));
|
|
16
|
+
}
|
|
17
|
+
catch { }
|
|
15
18
|
}
|
|
16
19
|
return cookies;
|
|
17
20
|
}
|
package/dist/web-static.js
CHANGED
|
@@ -54,7 +54,29 @@ export async function serveStatic(response, requestPath) {
|
|
|
54
54
|
stream.pipe(response);
|
|
55
55
|
return true;
|
|
56
56
|
}
|
|
57
|
-
|
|
57
|
+
function parseRangeHeader(rangeHeader, size) {
|
|
58
|
+
if (!rangeHeader)
|
|
59
|
+
return undefined;
|
|
60
|
+
const match = /^bytes=(\d*)-(\d*)$/.exec(rangeHeader.trim());
|
|
61
|
+
if (!match)
|
|
62
|
+
return undefined;
|
|
63
|
+
const [, rawStart, rawEnd] = match;
|
|
64
|
+
if (!rawStart && !rawEnd)
|
|
65
|
+
return undefined;
|
|
66
|
+
if (!rawStart) {
|
|
67
|
+
const suffixLength = Number(rawEnd);
|
|
68
|
+
if (!Number.isSafeInteger(suffixLength) || suffixLength <= 0)
|
|
69
|
+
return undefined;
|
|
70
|
+
return { start: Math.max(0, size - suffixLength), end: size - 1 };
|
|
71
|
+
}
|
|
72
|
+
const start = Number(rawStart);
|
|
73
|
+
const end = rawEnd ? Number(rawEnd) : size - 1;
|
|
74
|
+
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start || start >= size) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
return { start, end: Math.min(end, size - 1) };
|
|
78
|
+
}
|
|
79
|
+
export async function serveAttachment(config, response, requestPath, rangeHeader) {
|
|
58
80
|
const relativePath = decodeURIComponent(requestPath.replace(/^\/api\/web\/attachments\/?/, ""));
|
|
59
81
|
if (!relativePath) {
|
|
60
82
|
sendText(response, 404, "Not found");
|
|
@@ -93,9 +115,22 @@ export async function serveAttachment(config, response, requestPath) {
|
|
|
93
115
|
const fileStat = await stat(fileRealPath).catch(() => undefined);
|
|
94
116
|
if (!fileStat?.isFile())
|
|
95
117
|
continue;
|
|
118
|
+
const range = parseRangeHeader(rangeHeader, fileStat.size);
|
|
119
|
+
if (range) {
|
|
120
|
+
response.writeHead(206, {
|
|
121
|
+
"content-type": mimeType(filePath),
|
|
122
|
+
"content-length": String(range.end - range.start + 1),
|
|
123
|
+
"content-range": `bytes ${range.start}-${range.end}/${fileStat.size}`,
|
|
124
|
+
"accept-ranges": "bytes",
|
|
125
|
+
"cache-control": "private, max-age=31536000, immutable",
|
|
126
|
+
});
|
|
127
|
+
createReadStream(fileRealPath, { start: range.start, end: range.end }).pipe(response);
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
96
130
|
response.writeHead(200, {
|
|
97
131
|
"content-type": mimeType(filePath),
|
|
98
132
|
"content-length": String(fileStat.size),
|
|
133
|
+
"accept-ranges": "bytes",
|
|
99
134
|
"cache-control": "private, max-age=31536000, immutable",
|
|
100
135
|
});
|
|
101
136
|
createReadStream(fileRealPath).pipe(response);
|
package/dist/web-tools.js
CHANGED
|
@@ -69,7 +69,7 @@ class PageCache {
|
|
|
69
69
|
this.entries.delete(url);
|
|
70
70
|
return undefined;
|
|
71
71
|
}
|
|
72
|
-
entry.
|
|
72
|
+
entry.lastAccessed = Date.now();
|
|
73
73
|
this.entries.delete(url);
|
|
74
74
|
this.entries.set(url, entry);
|
|
75
75
|
return entry;
|
|
@@ -79,10 +79,12 @@ class PageCache {
|
|
|
79
79
|
return;
|
|
80
80
|
if (this.entries.has(url))
|
|
81
81
|
this.entries.delete(url);
|
|
82
|
+
const now = Date.now();
|
|
82
83
|
this.entries.set(url, {
|
|
83
84
|
content,
|
|
84
85
|
provider,
|
|
85
|
-
fetchedAt:
|
|
86
|
+
fetchedAt: now,
|
|
87
|
+
lastAccessed: now,
|
|
86
88
|
});
|
|
87
89
|
while (this.entries.size > this.capacity) {
|
|
88
90
|
const oldest = this.entries.keys().next().value;
|
|
@@ -382,7 +384,7 @@ function formatFetchContent(url, provider, chunk) {
|
|
|
382
384
|
chunk.text,
|
|
383
385
|
];
|
|
384
386
|
if (chunk.hasMore && chunk.nextOffset !== undefined) {
|
|
385
|
-
lines.push("", `[More content available. Next chunk:
|
|
387
|
+
lines.push("", `[More content available. Next chunk: fetch_web(url="${url}", offset=${chunk.nextOffset})]`);
|
|
386
388
|
}
|
|
387
389
|
return prefixUntrustedWebContent(lines.join("\n"));
|
|
388
390
|
}
|
|
@@ -778,7 +780,7 @@ function makeSearchTool(config) {
|
|
|
778
780
|
if (config.apiKeys.EXA_API_KEY)
|
|
779
781
|
providers.exa = createExaProvider(config.apiKeys.EXA_API_KEY);
|
|
780
782
|
return {
|
|
781
|
-
name: "
|
|
783
|
+
name: "search_web",
|
|
782
784
|
label: "Web Search",
|
|
783
785
|
description: "look something up on the open web. returns titles, urls, snippets, and dates when present. depth=thorough swaps brevity for inline excerpts.",
|
|
784
786
|
parameters: webSearchSchema,
|
|
@@ -847,7 +849,7 @@ function makeSearchTool(config) {
|
|
|
847
849
|
function makeFetchTool(config) {
|
|
848
850
|
const providers = createFetchProviders(config);
|
|
849
851
|
return {
|
|
850
|
-
name: "
|
|
852
|
+
name: "fetch_web",
|
|
851
853
|
label: "Web Fetch",
|
|
852
854
|
description: "pull a webpage down as clean markdown.",
|
|
853
855
|
parameters: webFetchSchema,
|
|
@@ -923,6 +925,7 @@ export function createWebTools(_config) {
|
|
|
923
925
|
}
|
|
924
926
|
export const __webToolsTest = {
|
|
925
927
|
PageCache,
|
|
928
|
+
createWebTools,
|
|
926
929
|
createTestSearchProvider,
|
|
927
930
|
createFetchProviders,
|
|
928
931
|
formatFetchContent,
|