threadnote 1.1.1 → 1.1.2
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/dist/mcp_server.cjs +21 -6
- package/dist/threadnote.cjs +99 -11
- package/package.json +1 -1
package/dist/mcp_server.cjs
CHANGED
|
@@ -36134,7 +36134,13 @@ function recallSnippet(value) {
|
|
|
36134
36134
|
const oneLine = value.replace(/\s+/g, " ").trim();
|
|
36135
36135
|
return oneLine.length > 180 ? `${oneLine.slice(0, 180)}\u2026` : oneLine;
|
|
36136
36136
|
}
|
|
36137
|
-
function
|
|
36137
|
+
function isArchivedMemoryUri(uri) {
|
|
36138
|
+
const documentUri = uri.replace(/#.*$/, "");
|
|
36139
|
+
return /^viking:\/\/user\/[^/]+\/memories\/(?:durable|handoffs|incidents|preferences|smoke)\/archived(?:\/|$)/.test(
|
|
36140
|
+
documentUri
|
|
36141
|
+
);
|
|
36142
|
+
}
|
|
36143
|
+
function parseRecallHits(output, options = {}) {
|
|
36138
36144
|
const start = output.search(/^\{/m);
|
|
36139
36145
|
if (start < 0) {
|
|
36140
36146
|
return [];
|
|
@@ -36155,6 +36161,9 @@ function parseRecallHits(output) {
|
|
|
36155
36161
|
if (!isJsonObject(item) || typeof item.uri !== "string" || isSummarySidecarUri(item.uri)) {
|
|
36156
36162
|
continue;
|
|
36157
36163
|
}
|
|
36164
|
+
if (options.includeArchived !== true && isArchivedMemoryUri(item.uri)) {
|
|
36165
|
+
continue;
|
|
36166
|
+
}
|
|
36158
36167
|
hits.push({
|
|
36159
36168
|
contextType: typeof item.context_type === "string" ? item.context_type : "result",
|
|
36160
36169
|
score: typeof item.score === "number" ? item.score : 0,
|
|
@@ -38417,7 +38426,7 @@ function registerSearchTool(server, config2, name, description) {
|
|
|
38417
38426
|
uri: external_exports.string().optional().describe("Optional viking:// subtree to search"),
|
|
38418
38427
|
callerCwd: external_exports.string().optional().describe("Optional absolute caller workspace path used to resolve this/current branch queries"),
|
|
38419
38428
|
nodeLimit: external_exports.number().int().positive().max(100).optional().describe("Maximum result count"),
|
|
38420
|
-
includeArchived: external_exports.boolean().optional().describe("Include archived memories in
|
|
38429
|
+
includeArchived: external_exports.boolean().optional().describe("Include archived memories in recall results"),
|
|
38421
38430
|
threshold: external_exports.number().min(0).max(1).optional().describe(
|
|
38422
38431
|
"Minimum relevance score 0-1 (default 0.5); lower it (toward 0) to broaden when a recall comes back empty"
|
|
38423
38432
|
)
|
|
@@ -38473,14 +38482,20 @@ async function runRecallTool(config2, params) {
|
|
|
38473
38482
|
const limitArgs = params.nodeLimit ? ["--node-limit", String(params.nodeLimit)] : [];
|
|
38474
38483
|
const threshold = params.threshold ?? RECALL_SCORE_THRESHOLD;
|
|
38475
38484
|
const pinnedArgs = params.pinnedUri ? ["--uri", params.pinnedUri] : [];
|
|
38476
|
-
const base = await recallSearchHits(
|
|
38485
|
+
const base = await recallSearchHits(
|
|
38486
|
+
config2,
|
|
38487
|
+
["search", query, ...pinnedArgs, ...limitArgs],
|
|
38488
|
+
threshold,
|
|
38489
|
+
params.includeArchived
|
|
38490
|
+
);
|
|
38477
38491
|
const passes = [base.hits];
|
|
38478
38492
|
const seededUri = project ? trimTrailingSlash(project.uri) : void 0;
|
|
38479
38493
|
if (seededUri?.startsWith("viking://") && seededUri !== params.pinnedUri) {
|
|
38480
38494
|
const seeded = await recallSearchHits(
|
|
38481
38495
|
config2,
|
|
38482
38496
|
["search", params.query, "--uri", seededUri, ...limitArgs],
|
|
38483
|
-
threshold
|
|
38497
|
+
threshold,
|
|
38498
|
+
params.includeArchived
|
|
38484
38499
|
);
|
|
38485
38500
|
passes.push(seeded.hits);
|
|
38486
38501
|
}
|
|
@@ -38514,7 +38529,7 @@ async function runRecallTool(config2, params) {
|
|
|
38514
38529
|
const onlyErrorNote = !base.ok && !semanticSection && sections.length === 1;
|
|
38515
38530
|
return { content: [{ type: "text", text: sections.join("\n\n") }], isError: onlyErrorNote || void 0 };
|
|
38516
38531
|
}
|
|
38517
|
-
async function recallSearchHits(config2, searchArgs, threshold) {
|
|
38532
|
+
async function recallSearchHits(config2, searchArgs, threshold, includeArchived) {
|
|
38518
38533
|
let result = await runOpenVikingTool(config2, [
|
|
38519
38534
|
...searchArgs,
|
|
38520
38535
|
"--threshold",
|
|
@@ -38532,7 +38547,7 @@ async function recallSearchHits(config2, searchArgs, threshold) {
|
|
|
38532
38547
|
if (result.isError === true) {
|
|
38533
38548
|
return { errorText: text.trim(), hits: [], ok: false };
|
|
38534
38549
|
}
|
|
38535
|
-
return { errorText: "", hits: parseRecallHits(text), ok: true };
|
|
38550
|
+
return { errorText: "", hits: parseRecallHits(text, { includeArchived }), ok: true };
|
|
38536
38551
|
}
|
|
38537
38552
|
async function recallHygieneHintsSection(config2, recallText) {
|
|
38538
38553
|
const uris = activePersonalMemoryUrisFromText(recallText, config2.user);
|
package/dist/threadnote.cjs
CHANGED
|
@@ -3473,6 +3473,7 @@ var {
|
|
|
3473
3473
|
var DEFAULT_HOST = "127.0.0.1";
|
|
3474
3474
|
var DEFAULT_PORT = 1933;
|
|
3475
3475
|
var DEFAULT_OPENVIKING_VERSION = "0.3.24";
|
|
3476
|
+
var OPENVIKING_TOOL_PYTHON = "3.12";
|
|
3476
3477
|
var DEFAULT_ACCOUNT = "local";
|
|
3477
3478
|
var DEFAULT_AGENT_ID = "threadnote";
|
|
3478
3479
|
var OPENVIKING_PACKAGE_NAME = "openviking[local-embed]";
|
|
@@ -4315,7 +4316,13 @@ function recallSnippet(value) {
|
|
|
4315
4316
|
const oneLine = value.replace(/\s+/g, " ").trim();
|
|
4316
4317
|
return oneLine.length > 180 ? `${oneLine.slice(0, 180)}\u2026` : oneLine;
|
|
4317
4318
|
}
|
|
4318
|
-
function
|
|
4319
|
+
function isArchivedMemoryUri(uri) {
|
|
4320
|
+
const documentUri = uri.replace(/#.*$/, "");
|
|
4321
|
+
return /^viking:\/\/user\/[^/]+\/memories\/(?:durable|handoffs|incidents|preferences|smoke)\/archived(?:\/|$)/.test(
|
|
4322
|
+
documentUri
|
|
4323
|
+
);
|
|
4324
|
+
}
|
|
4325
|
+
function parseRecallHits(output2, options = {}) {
|
|
4319
4326
|
const start = output2.search(/^\{/m);
|
|
4320
4327
|
if (start < 0) {
|
|
4321
4328
|
return [];
|
|
@@ -4336,6 +4343,9 @@ function parseRecallHits(output2) {
|
|
|
4336
4343
|
if (!isJsonObject(item) || typeof item.uri !== "string" || isSummarySidecarUri(item.uri)) {
|
|
4337
4344
|
continue;
|
|
4338
4345
|
}
|
|
4346
|
+
if (options.includeArchived !== true && isArchivedMemoryUri(item.uri)) {
|
|
4347
|
+
continue;
|
|
4348
|
+
}
|
|
4339
4349
|
hits.push({
|
|
4340
4350
|
contextType: typeof item.context_type === "string" ? item.context_type : "result",
|
|
4341
4351
|
score: typeof item.score === "number" ? item.score : 0,
|
|
@@ -9960,14 +9970,17 @@ async function runRecall(config, options) {
|
|
|
9960
9970
|
if (inferredUri) {
|
|
9961
9971
|
console.log(`Recall scope: ${inferredUri}`);
|
|
9962
9972
|
}
|
|
9963
|
-
const
|
|
9973
|
+
const includeArchived = options.includeArchived === true;
|
|
9974
|
+
const passes = [
|
|
9975
|
+
await recallSearchHits(config, ov, searchArgs(inferredUri), { dryRun, includeArchived })
|
|
9976
|
+
];
|
|
9964
9977
|
if (options.project && project) {
|
|
9965
9978
|
const projectMemoryUri = `viking://user/${uriSegment(config.user)}/memories/durable/projects/${uriSegment(project.name)}`;
|
|
9966
|
-
passes.push(await recallSearchHits(config, ov, searchArgs(projectMemoryUri), { dryRun }));
|
|
9979
|
+
passes.push(await recallSearchHits(config, ov, searchArgs(projectMemoryUri), { dryRun, includeArchived }));
|
|
9967
9980
|
}
|
|
9968
9981
|
const seededUri = project ? trimTrailingSlash(project.uri) : void 0;
|
|
9969
9982
|
if (seededUri?.startsWith("viking://") && seededUri !== inferredUri && !options.uri && options.inferScope !== false) {
|
|
9970
|
-
passes.push(await recallSearchHits(config, ov, searchArgs(seededUri), { dryRun }));
|
|
9983
|
+
passes.push(await recallSearchHits(config, ov, searchArgs(seededUri), { dryRun, includeArchived }));
|
|
9971
9984
|
}
|
|
9972
9985
|
const recallOutputs = [];
|
|
9973
9986
|
const semanticSection = formatRecallHits(mergeRecallHits(passes), nodeLimit ?? 12);
|
|
@@ -9978,7 +9991,7 @@ ${semanticSection}`);
|
|
|
9978
9991
|
}
|
|
9979
9992
|
const exactOutput = await printExactMemoryMatches(config, ov, query, {
|
|
9980
9993
|
dryRun,
|
|
9981
|
-
includeArchived
|
|
9994
|
+
includeArchived,
|
|
9982
9995
|
project
|
|
9983
9996
|
});
|
|
9984
9997
|
if (exactOutput) {
|
|
@@ -10002,7 +10015,7 @@ async function recallSearchHits(config, ov, args, options) {
|
|
|
10002
10015
|
console.log(`WARN recall search failed: ${result.stderr.trim() || result.stdout.trim() || "ov search error"}`);
|
|
10003
10016
|
return [];
|
|
10004
10017
|
}
|
|
10005
|
-
return parseRecallHits(result.stdout);
|
|
10018
|
+
return parseRecallHits(result.stdout, { includeArchived: options.includeArchived });
|
|
10006
10019
|
}
|
|
10007
10020
|
function stripAdvancedSearchFlags(args) {
|
|
10008
10021
|
const stripped = [];
|
|
@@ -12442,6 +12455,14 @@ async function runInstall(config, options) {
|
|
|
12442
12455
|
serverInstallRan = true;
|
|
12443
12456
|
}
|
|
12444
12457
|
const resolvedServerPath = serverInstallRan ? await findOpenVikingServer() : serverPath;
|
|
12458
|
+
if (serverInstallRan && !resolvedServerPath && !dryRun) {
|
|
12459
|
+
const message = `OpenViking install ran but ${OPENVIKING_SERVER_COMMAND} was not found on PATH, in the uv tool bin dir, or ~/.local/bin. Re-run \`threadnote install --force\` (it streams the full build), then \`threadnote doctor\`.`;
|
|
12460
|
+
if (options.requireServerBinary === false) {
|
|
12461
|
+
console.warn(`WARN ${message}`);
|
|
12462
|
+
} else {
|
|
12463
|
+
throw new Error(message);
|
|
12464
|
+
}
|
|
12465
|
+
}
|
|
12445
12466
|
if (resolvedServerPath && !dryRun) {
|
|
12446
12467
|
await maybePrintOpenVikingPathHint(resolvedServerPath);
|
|
12447
12468
|
}
|
|
@@ -12478,6 +12499,7 @@ async function runRepair(config, options) {
|
|
|
12478
12499
|
packageManager: options.packageManager,
|
|
12479
12500
|
printNextSteps: false,
|
|
12480
12501
|
repairInvalidConfigs: true,
|
|
12502
|
+
requireServerBinary: false,
|
|
12481
12503
|
start: false
|
|
12482
12504
|
});
|
|
12483
12505
|
await repairManifest(config, dryRun);
|
|
@@ -12832,7 +12854,11 @@ async function openVikingServerCheck() {
|
|
|
12832
12854
|
const name = OPENVIKING_SERVER_COMMAND;
|
|
12833
12855
|
const executable = await findOpenVikingServer();
|
|
12834
12856
|
if (!executable) {
|
|
12835
|
-
return {
|
|
12857
|
+
return {
|
|
12858
|
+
name,
|
|
12859
|
+
status: "fail",
|
|
12860
|
+
detail: "missing; run `threadnote install` to fetch it via uv or pipx (local-embed may compile from source on first install)"
|
|
12861
|
+
};
|
|
12836
12862
|
}
|
|
12837
12863
|
const result = await runCommand(executable, ["--help"], { allowFailure: true });
|
|
12838
12864
|
const onPath = await findExecutable([OPENVIKING_SERVER_COMMAND]);
|
|
@@ -13066,7 +13092,36 @@ async function runInstallCommands(config, preferred, force, dryRun) {
|
|
|
13066
13092
|
}
|
|
13067
13093
|
const installCommands = await getInstallCommands(config, manager, force);
|
|
13068
13094
|
for (const installCommand of installCommands) {
|
|
13069
|
-
|
|
13095
|
+
if (dryRun) {
|
|
13096
|
+
await maybeRun(true, installCommand.executable, installCommand.args);
|
|
13097
|
+
continue;
|
|
13098
|
+
}
|
|
13099
|
+
console.log(`Running: ${formatShellCommand(installCommand.executable, installCommand.args)}`);
|
|
13100
|
+
const exitCode = await runInteractive(installCommand.executable, installCommand.args);
|
|
13101
|
+
if (exitCode !== 0) {
|
|
13102
|
+
printOpenVikingInstallFailureHelp(installCommand);
|
|
13103
|
+
throw new Error(`${formatShellCommand(installCommand.executable, installCommand.args)} exited with ${exitCode}.`);
|
|
13104
|
+
}
|
|
13105
|
+
}
|
|
13106
|
+
}
|
|
13107
|
+
function printOpenVikingInstallFailureHelp(failedCommand) {
|
|
13108
|
+
console.error("");
|
|
13109
|
+
console.error("OpenViking install did not complete.");
|
|
13110
|
+
console.error(
|
|
13111
|
+
"openviking[local-embed] includes llama-cpp-python, which compiles from source when no prebuilt wheel matches your Python/platform \u2014 that build can run 10-20 minutes and is memory-heavy, so it may be killed by the OS (out of memory) or look stuck."
|
|
13112
|
+
);
|
|
13113
|
+
console.error("Re-run it directly to see full output without any wrapper timeout:");
|
|
13114
|
+
console.error(` ${formatShellCommand(failedCommand.executable, failedCommand.args)}`);
|
|
13115
|
+
console.error("Cap memory use during the compile with: CMAKE_BUILD_PARALLEL_LEVEL=2 <command above>");
|
|
13116
|
+
if (failedCommand.executable === "uv") {
|
|
13117
|
+
if (failedCommand.args.includes("--python")) {
|
|
13118
|
+
console.error(
|
|
13119
|
+
"If uv could not fetch a managed CPython (offline or restricted network), drop the version pin and retry: THREADNOTE_OPENVIKING_PYTHON= threadnote install --force"
|
|
13120
|
+
);
|
|
13121
|
+
}
|
|
13122
|
+
console.error("If it was killed mid-build, clear the partial install first, then retry:");
|
|
13123
|
+
console.error(" uv cache clean");
|
|
13124
|
+
console.error(' rm -rf "$(uv tool dir)/openviking"');
|
|
13070
13125
|
}
|
|
13071
13126
|
}
|
|
13072
13127
|
async function offerToInstallUv() {
|
|
@@ -13147,12 +13202,33 @@ async function getPythonSystemCertificatesInstallCommand(serverPath) {
|
|
|
13147
13202
|
}
|
|
13148
13203
|
return { executable: pythonPath, args: ["-m", "pip", "install", PYTHON_SYSTEM_CERTS_PACKAGE] };
|
|
13149
13204
|
}
|
|
13205
|
+
function localEmbedWheelIndexUrl() {
|
|
13206
|
+
const override = process.env.THREADNOTE_LLAMA_WHEEL_INDEX;
|
|
13207
|
+
if (override !== void 0) {
|
|
13208
|
+
return override.trim() === "" ? void 0 : override.trim();
|
|
13209
|
+
}
|
|
13210
|
+
const base = "https://abetlen.github.io/llama-cpp-python/whl";
|
|
13211
|
+
return (0, import_node_os6.platform)() === "darwin" ? `${base}/metal` : `${base}/cpu`;
|
|
13212
|
+
}
|
|
13213
|
+
function openVikingToolPython() {
|
|
13214
|
+
const override = process.env.THREADNOTE_OPENVIKING_PYTHON;
|
|
13215
|
+
if (override !== void 0) {
|
|
13216
|
+
return override.trim() === "" ? void 0 : override.trim();
|
|
13217
|
+
}
|
|
13218
|
+
return OPENVIKING_TOOL_PYTHON;
|
|
13219
|
+
}
|
|
13150
13220
|
async function getInstallCommands(config, preferred, force) {
|
|
13151
13221
|
const packageSpec = `${OPENVIKING_PACKAGE_NAME}==${config.openVikingVersion}`;
|
|
13222
|
+
const wheelIndex = localEmbedWheelIndexUrl();
|
|
13152
13223
|
const manager = preferred ?? await detectPackageManager();
|
|
13153
13224
|
if (manager === "pipx") {
|
|
13225
|
+
const installArgs = force ? ["install", "--force"] : ["install"];
|
|
13226
|
+
if (wheelIndex) {
|
|
13227
|
+
installArgs.push("--pip-args", `--extra-index-url ${wheelIndex}`);
|
|
13228
|
+
}
|
|
13229
|
+
installArgs.push(packageSpec);
|
|
13154
13230
|
return [
|
|
13155
|
-
{ executable: "pipx", args:
|
|
13231
|
+
{ executable: "pipx", args: installArgs },
|
|
13156
13232
|
{
|
|
13157
13233
|
executable: "pipx",
|
|
13158
13234
|
args: force ? ["inject", "--force", "openviking", PYTHON_SYSTEM_CERTS_PACKAGE] : ["inject", "openviking", PYTHON_SYSTEM_CERTS_PACKAGE]
|
|
@@ -13160,7 +13236,16 @@ async function getInstallCommands(config, preferred, force) {
|
|
|
13160
13236
|
];
|
|
13161
13237
|
}
|
|
13162
13238
|
if (manager === "uv") {
|
|
13163
|
-
const
|
|
13239
|
+
const toolPython = openVikingToolPython();
|
|
13240
|
+
const uvArgs = [
|
|
13241
|
+
"tool",
|
|
13242
|
+
"install",
|
|
13243
|
+
"--native-tls",
|
|
13244
|
+
...toolPython ? ["--python", toolPython] : [],
|
|
13245
|
+
"--with",
|
|
13246
|
+
PYTHON_SYSTEM_CERTS_PACKAGE,
|
|
13247
|
+
...wheelIndex ? ["--extra-index-url", wheelIndex] : []
|
|
13248
|
+
];
|
|
13164
13249
|
return [
|
|
13165
13250
|
{
|
|
13166
13251
|
executable: "uv",
|
|
@@ -13172,6 +13257,9 @@ async function getInstallCommands(config, preferred, force) {
|
|
|
13172
13257
|
if (force) {
|
|
13173
13258
|
pipArgs.push("--upgrade", "--force-reinstall");
|
|
13174
13259
|
}
|
|
13260
|
+
if (wheelIndex) {
|
|
13261
|
+
pipArgs.push("--extra-index-url", wheelIndex);
|
|
13262
|
+
}
|
|
13175
13263
|
pipArgs.push(PYTHON_SYSTEM_CERTS_PACKAGE);
|
|
13176
13264
|
pipArgs.push(packageSpec);
|
|
13177
13265
|
return [{ executable: "python3", args: pipArgs }];
|
|
@@ -14690,7 +14778,7 @@ async function main() {
|
|
|
14690
14778
|
program2.command("migrate-lifecycle").description("Move clear legacy handoff memories into lifecycle-aware archive paths").option("--apply", "Perform the migration; without this, prints a dry run").option("--dry-run", "Print migration actions without writing or removing memories").option("--limit <count>", "Maximum number of legacy handoffs to migrate").action(async (options) => {
|
|
14691
14779
|
await runMigrateLifecycle(getRuntimeConfig(program2), options);
|
|
14692
14780
|
});
|
|
14693
|
-
program2.command("recall").description("Search shared OpenViking context").requiredOption("--query <query>", "Search query").option("--dry-run", "Print ov command without searching").option("--include-archived", "Include archived memories in
|
|
14781
|
+
program2.command("recall").description("Search shared OpenViking context").requiredOption("--query <query>", "Search query").option("--dry-run", "Print ov command without searching").option("--include-archived", "Include archived memories in recall results").option("-n, --node-limit <count>", "Maximum number of search results").option("--no-infer-scope", "Disable query-based scope inference").option("--project <name>", "Prioritize a project: add a scoped pass over its memories alongside the global search").option("--threshold <score>", "Minimum relevance score 0-1 (default 0.45); lower to broaden when recall is empty").option("--uri <uri>", "Restrict search to a viking:// URI").action(async (options) => {
|
|
14694
14782
|
await runRecall(getRuntimeConfig(program2), options);
|
|
14695
14783
|
});
|
|
14696
14784
|
program2.command("compact").description("Plan or apply scoped memory hygiene for active personal memories").requiredOption("--project <name>", "Project/repo namespace to inspect").option("--apply", "Apply the compact plan; without this, prints a dry run").option("--dry-run", "Print the compact plan without changing anything").option("--kind <kind>", "durable, handoff, or incident", parseCompactKind).option("--topic <name>", "Stable topic name to inspect").action(async (options) => {
|