Fix build errors

This commit is contained in:
Vignesh Natarajan
2026-01-27 23:08:43 -08:00
committed by Vignesh
parent 3a57106c1e
commit e12184661e
4 changed files with 68 additions and 32 deletions

View File

@@ -109,6 +109,13 @@ out to QMD for retrieval. Key points:
a release) and make sure the `qmd` binary is on the gateways `PATH`.
- QMD needs an SQLite build that allows extensions (`brew install sqlite` on
macOS). The gateway sets `INDEX_PATH`/`QMD_CONFIG_DIR` automatically.
- QMD shells out to its CLI, which depends on an Ollama daemon listening on
`http://localhost:11434` to load the bundled models (`embeddinggemma`,
`ExpedientFalcon/qwen3-reranker:0.6b-q8_0`, `qwen3:0.6b`). Install/run Ollama
separately before enabling the backend.
- OS support: macOS and Linux work out of the box once Bun + SQLite + Ollama are
installed. Windows requires WSL2 (or building the experimental Ollama port)
until Ollama ships native binaries.
**How the sidecar runs**
- The gateway writes a self-contained QMD home under

View File

@@ -257,13 +257,16 @@ export async function runMemoryStatus(opts: MemoryCommandOptions) {
onMissing: (error) => defaultRuntime.log(error ?? "Memory search disabled."),
onCloseError: (err) =>
defaultRuntime.error(`Memory manager close failed: ${formatErrorMessage(err)}`),
close: (manager) => manager.close(),
close: async (manager) => {
await manager.close?.();
},
run: async (manager) => {
const deep = Boolean(opts.deep || opts.index);
let embeddingProbe:
| Awaited<ReturnType<typeof manager.probeEmbeddingAvailability>>
| undefined;
let indexError: string | undefined;
const syncFn = manager.sync;
if (deep) {
await withProgress({ label: "Checking memory…", total: 2 }, async (progress) => {
progress.setLabel("Probing vector…");
@@ -273,7 +276,7 @@ export async function runMemoryStatus(opts: MemoryCommandOptions) {
embeddingProbe = await manager.probeEmbeddingAvailability();
progress.tick();
});
if (opts.index) {
if (opts.index && syncFn) {
await withProgressTotals(
{
label: "Indexing memory…",
@@ -282,7 +285,7 @@ export async function runMemoryStatus(opts: MemoryCommandOptions) {
},
async (update, progress) => {
try {
await manager.sync({
await syncFn({
reason: "cli",
force: true,
progress: (syncUpdate) => {
@@ -303,6 +306,8 @@ export async function runMemoryStatus(opts: MemoryCommandOptions) {
}
},
);
} else if (opts.index && !syncFn) {
defaultRuntime.log("Memory backend does not support manual reindex.");
}
} else {
await manager.probeVectorAvailability();
@@ -311,12 +316,10 @@ export async function runMemoryStatus(opts: MemoryCommandOptions) {
const sources = (
status.sources?.length ? status.sources : ["memory"]
) as MemorySourceName[];
const scan = await scanMemorySources({
workspaceDir: status.workspaceDir,
agentId,
sources,
extraPaths: status.extraPaths,
});
const workspaceDir = status.workspaceDir;
const scan = workspaceDir
? await scanMemorySources({ workspaceDir, agentId, sources, extraPaths: status.extraPaths })
: undefined;
allResults.push({ agentId, status, embeddingProbe, indexError, scan });
},
});
@@ -338,28 +341,35 @@ export async function runMemoryStatus(opts: MemoryCommandOptions) {
for (const result of allResults) {
const { agentId, status, embeddingProbe, indexError, scan } = result;
const filesIndexed = status.files ?? 0;
const chunksIndexed = status.chunks ?? 0;
const totalFiles = scan?.totalFiles ?? null;
const indexedLabel =
totalFiles === null
? `${status.files}/? files · ${status.chunks} chunks`
: `${status.files}/${totalFiles} files · ${status.chunks} chunks`;
? `${filesIndexed}/? files · ${chunksIndexed} chunks`
: `${filesIndexed}/${totalFiles} files · ${chunksIndexed} chunks`;
if (opts.index) {
const line = indexError ? `Memory index failed: ${indexError}` : "Memory index complete.";
defaultRuntime.log(line);
}
const extraPaths = formatExtraPaths(status.workspaceDir, status.extraPaths ?? []);
const requestedProvider = status.requestedProvider ?? status.provider;
const modelLabel = status.model ?? status.provider;
const storePath = status.dbPath ? shortenHomePath(status.dbPath) : "<unknown>";
const workspacePath = status.workspaceDir ? shortenHomePath(status.workspaceDir) : "<unknown>";
const sourceList = status.sources?.length ? status.sources.join(", ") : null;
const extraPaths = status.workspaceDir
? formatExtraPaths(status.workspaceDir, status.extraPaths ?? [])
: [];
const lines = [
`${heading("Memory Search")} ${muted(`(${agentId})`)}`,
`${label("Provider")} ${info(status.provider)} ${muted(
`(requested: ${status.requestedProvider})`,
)}`,
`${label("Model")} ${info(status.model)}`,
status.sources?.length ? `${label("Sources")} ${info(status.sources.join(", "))}` : null,
`${label("Provider")} ${info(status.provider)} ${muted(`(requested: ${requestedProvider})`)}`,
`${label("Model")} ${info(modelLabel)}`,
sourceList ? `${label("Sources")} ${info(sourceList)}` : null,
extraPaths.length ? `${label("Extra paths")} ${info(extraPaths.join(", "))}` : null,
`${label("Indexed")} ${success(indexedLabel)}`,
`${label("Dirty")} ${status.dirty ? warn("yes") : muted("no")}`,
`${label("Store")} ${info(shortenHomePath(status.dbPath))}`,
`${label("Workspace")} ${info(shortenHomePath(status.workspaceDir))}`,
`${label("Store")} ${info(storePath)}`,
`${label("Workspace")} ${info(workspacePath)}`,
].filter(Boolean) as string[];
if (embeddingProbe) {
const state = embeddingProbe.ok ? "ready" : "unavailable";
@@ -372,7 +382,7 @@ export async function runMemoryStatus(opts: MemoryCommandOptions) {
if (status.sourceCounts?.length) {
lines.push(label("By source"));
for (const entry of status.sourceCounts) {
const total = scan?.sources.find(
const total = scan?.sources?.find(
(scanEntry) => scanEntry.source === entry.source,
)?.totalFiles;
const counts =
@@ -504,9 +514,12 @@ export function registerMemoryCli(program: Command) {
onMissing: (error) => defaultRuntime.log(error ?? "Memory search disabled."),
onCloseError: (err) =>
defaultRuntime.error(`Memory manager close failed: ${formatErrorMessage(err)}`),
close: (manager) => manager.close(),
close: async (manager) => {
await manager.close?.();
},
run: async (manager) => {
try {
const syncFn = manager.sync;
if (opts.verbose) {
const status = manager.status();
const rich = isRich();
@@ -515,16 +528,20 @@ export function registerMemoryCli(program: Command) {
const info = (text: string) => colorize(rich, theme.info, text);
const warn = (text: string) => colorize(rich, theme.warn, text);
const label = (text: string) => muted(`${text}:`);
const sourceLabels = status.sources.map((source) =>
formatSourceLabel(source, status.workspaceDir, agentId),
const sourceLabels = (status.sources ?? []).map((source) =>
formatSourceLabel(source, status.workspaceDir ?? "", agentId),
);
const extraPaths = formatExtraPaths(status.workspaceDir, status.extraPaths ?? []);
const extraPaths = status.workspaceDir
? formatExtraPaths(status.workspaceDir, status.extraPaths ?? [])
: [];
const requestedProvider = status.requestedProvider ?? status.provider;
const modelLabel = status.model ?? status.provider;
const lines = [
`${heading("Memory Index")} ${muted(`(${agentId})`)}`,
`${label("Provider")} ${info(status.provider)} ${muted(
`(requested: ${status.requestedProvider})`,
`(requested: ${requestedProvider})`,
)}`,
`${label("Model")} ${info(status.model)}`,
`${label("Model")} ${info(modelLabel)}`,
sourceLabels.length
? `${label("Sources")} ${info(sourceLabels.join(", "))}`
: null,
@@ -571,6 +588,10 @@ export function registerMemoryCli(program: Command) {
? `${lastLabel} · elapsed ${elapsed} · eta ${eta}`
: `${lastLabel} · elapsed ${elapsed}`;
};
if (!syncFn) {
defaultRuntime.log("Memory backend does not support manual reindex.");
return;
}
await withProgressTotals(
{
label: "Indexing memory…",
@@ -582,7 +603,7 @@ export function registerMemoryCli(program: Command) {
progress.setLabel(buildLabel());
}, 1000);
try {
await manager.sync({
await syncFn({
reason: "cli",
force: true,
progress: (syncUpdate) => {
@@ -638,7 +659,9 @@ export function registerMemoryCli(program: Command) {
onMissing: (error) => defaultRuntime.log(error ?? "Memory search disabled."),
onCloseError: (err) =>
defaultRuntime.error(`Memory manager close failed: ${formatErrorMessage(err)}`),
close: (manager) => manager.close(),
close: async (manager) => {
await manager.close?.();
},
run: async (manager) => {
let results: Awaited<ReturnType<typeof manager.search>>;
try {

View File

@@ -86,10 +86,14 @@ export async function resolveDiscordTarget(
const likelyUsername = isLikelyUsername(trimmed);
const shouldLookup = isExplicitUserLookup(trimmed, parseOptions) || likelyUsername;
// Parse directly if it's already a known format. Use a safe parse so ambiguous
// numeric targets don't throw when we still want to attempt username lookup.
const directParse = safeParseDiscordTarget(trimmed, parseOptions);
if (directParse && directParse.kind !== "channel" && !likelyUsername) {
return directParse;
}
if (!shouldLookup) {
return directParse ?? parseDiscordTarget(trimmed, parseOptions);
}

View File

@@ -23,6 +23,8 @@ import type {
MemorySource,
MemorySyncProgressUpdate,
} from "./types.js";
type SqliteDatabase = import("node:sqlite").DatabaseSync;
import type { ResolvedMemoryBackendConfig, ResolvedQmdConfig } from "./backend-config.js";
const log = createSubsystemLogger("memory");
@@ -85,7 +87,7 @@ export class QmdMemoryManager implements MemorySearchManager {
private updateTimer: NodeJS.Timeout | null = null;
private pendingUpdate: Promise<void> | null = null;
private closed = false;
private db: import("node:sqlite").DatabaseSync | null = null;
private db: SqliteDatabase | null = null;
private lastUpdateAt: number | null = null;
private constructor(params: {
@@ -379,10 +381,10 @@ export class QmdMemoryManager implements MemorySearchManager {
});
}
private ensureDb() {
private ensureDb(): SqliteDatabase {
if (this.db) return this.db;
const sqlite = requireNodeSqlite();
this.db = sqlite.open(this.indexPath, { readonly: true });
const { DatabaseSync } = requireNodeSqlite();
this.db = new DatabaseSync(this.indexPath, { readOnly: true });
return this.db;
}