threadnote 1.4.3 → 1.4.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.
@@ -18,21 +18,6 @@
18
18
  "If Threadnote reported any original files were still being processed, rerun the printed threadnote forget <uri> command later."
19
19
  ],
20
20
  "requiresLegacyHandoffs": true
21
- },
22
- {
23
- "id": "ov-semantic-poison-hotfix-2734",
24
- "introducedIn": "1.4.3",
25
- "title": "Patch OpenViking against the semantic-queue poison loop (#2734)",
26
- "description": [
27
- "OpenViking 0.4.x can deadlock its semantic queue: a memory file reindexed with mode=semantic_and_vectors enqueues a directory-level semantic message whose URI is a file; the processor lists it, fails, and the message re-enqueues forever (it is AGFS-persisted, so it survives a server restart).",
28
- "This patches the installed OpenViking to skip non-directory/missing memory URIs (upstream fix PR #2735) and restarts the server so any stuck message drains on the next dequeue.",
29
- "It is a no-op when the installed OpenViking already includes the fix, a backup of the original file is kept, and the patched file is compile-checked before it is written."
30
- ],
31
- "commandArgs": ["repair-semantic-queue", "--apply"],
32
- "instructions": [
33
- "If the semantic queue was stuck it should now drain. Verify with: ov observer queue",
34
- "This is a temporary local patch; it is superseded automatically once Threadnote pins an OpenViking release that includes the fix. Re-run manually any time with: threadnote repair-semantic-queue --apply"
35
- ]
36
21
  }
37
22
  ]
38
23
  }
@@ -3394,7 +3394,7 @@ var program = new Command();
3394
3394
  // src/constants.ts
3395
3395
  var DEFAULT_HOST = "127.0.0.1";
3396
3396
  var DEFAULT_PORT = 1933;
3397
- var DEFAULT_OPENVIKING_VERSION = "0.4.4";
3397
+ var DEFAULT_OPENVIKING_VERSION = "0.4.5";
3398
3398
  var OPENVIKING_TOOL_PYTHON = "3.12";
3399
3399
  var DEFAULT_ACCOUNT = "local";
3400
3400
  var DEFAULT_AGENT_ID = "threadnote";
@@ -15009,199 +15009,6 @@ function parsePackageManager(value) {
15009
15009
  throw new Error(`Invalid package manager: ${value}`);
15010
15010
  }
15011
15011
 
15012
- // src/semantic_queue_repair.ts
15013
- var import_node_fs8 = require("node:fs");
15014
- var import_promises14 = require("node:fs/promises");
15015
- var import_node_os7 = require("node:os");
15016
- var import_node_path15 = require("node:path");
15017
- var HOTFIX_MARKER = "THREADNOTE-HOTFIX-2734";
15018
- var ENV_OVERRIDE = "THREADNOTE_OPENVIKING_SEMANTIC_PROCESSOR";
15019
- function patchSemanticProcessorSource(source) {
15020
- if (source.includes(HOTFIX_MARKER)) {
15021
- return { status: "already-fixed" };
15022
- }
15023
- const lines = source.split("\n");
15024
- const defIndex = lines.findIndex((line) => /^\s*async def _process_memory_directory\b/.test(line));
15025
- if (defIndex < 0) {
15026
- return { status: "no-anchor" };
15027
- }
15028
- const lsPattern = /^(\s+)entries = await viking_fs\.ls\(dir_uri\b/;
15029
- let lsIndex = -1;
15030
- let lsIndent = "";
15031
- for (let i = defIndex + 1; i < lines.length; i += 1) {
15032
- const match = lsPattern.exec(lines[i]);
15033
- if (match) {
15034
- lsIndex = i;
15035
- lsIndent = match[1];
15036
- break;
15037
- }
15038
- if (/^\s*async def \w/.test(lines[i])) {
15039
- break;
15040
- }
15041
- }
15042
- if (lsIndex < 0) {
15043
- return { status: "no-anchor" };
15044
- }
15045
- if (lines.slice(defIndex, lsIndex).some((line) => line.includes("viking_fs.stat(dir_uri"))) {
15046
- return { status: "already-fixed" };
15047
- }
15048
- let tryIndex = -1;
15049
- let base = "";
15050
- for (let j = lsIndex - 1; j > defIndex; j -= 1) {
15051
- const text = lines[j];
15052
- if (text.trim() === "" || text.trimStart().startsWith("#")) {
15053
- continue;
15054
- }
15055
- const match = /^(\s*)try:\s*$/.exec(text);
15056
- if (match && match[1].length < lsIndent.length) {
15057
- tryIndex = j;
15058
- base = match[1];
15059
- }
15060
- break;
15061
- }
15062
- if (tryIndex < 0) {
15063
- return { status: "no-anchor" };
15064
- }
15065
- const unit = " ".repeat(lsIndent.length - base.length);
15066
- const at = (level, text) => `${base}${unit.repeat(level)}${text}`;
15067
- const guard = [
15068
- at(0, `# ${HOTFIX_MARKER}: skip non-directory / missing memory URIs so a memory file`),
15069
- at(0, "# reindexed with mode=semantic_and_vectors cannot poison the semantic queue."),
15070
- at(0, "# Temporary local hot-fix; superseded once OpenViking ships upstream PR #2735."),
15071
- at(0, "try:"),
15072
- at(1, "_tn_dir_stat = await viking_fs.stat(dir_uri, ctx=ctx)"),
15073
- at(0, "except Exception as _tn_err:"),
15074
- at(1, 'if isinstance(_tn_err, FileNotFoundError) or "not found" in str(_tn_err).lower():'),
15075
- at(2, "_mark_done()"),
15076
- at(2, "return"),
15077
- at(1, "_tn_dir_stat = None"),
15078
- at(0, 'if _tn_dir_stat is not None and not _tn_dir_stat.get("isDir", _tn_dir_stat.get("is_dir", False)):'),
15079
- at(1, "_mark_done()"),
15080
- at(1, "return")
15081
- ];
15082
- const patched = [...lines.slice(0, tryIndex), ...guard, ...lines.slice(tryIndex)].join("\n");
15083
- return { status: "patched", source: patched };
15084
- }
15085
- async function semanticProcessorCandidates(root) {
15086
- const lib = (0, import_node_path15.join)(root, "lib");
15087
- if (!(0, import_node_fs8.existsSync)(lib)) {
15088
- return [];
15089
- }
15090
- let entries;
15091
- try {
15092
- entries = await (0, import_promises14.readdir)(lib);
15093
- } catch {
15094
- return [];
15095
- }
15096
- return entries.filter((name) => name.startsWith("python")).map((name) => (0, import_node_path15.join)(lib, name, "site-packages", "openviking", "storage", "queuefs", "semantic_processor.py"));
15097
- }
15098
- async function locateSemanticProcessorPath() {
15099
- const override = process.env[ENV_OVERRIDE]?.trim();
15100
- if (override) {
15101
- const resolved = expandPath(override);
15102
- if ((0, import_node_fs8.existsSync)(resolved)) {
15103
- return resolved;
15104
- }
15105
- }
15106
- const roots = [];
15107
- const server = await findOpenVikingServer();
15108
- if (server) {
15109
- roots.push((0, import_node_path15.dirname)((0, import_node_path15.dirname)(server)));
15110
- }
15111
- roots.push((0, import_node_path15.join)((0, import_node_os7.homedir)(), ".local", "share", "uv", "tools", "openviking"));
15112
- roots.push((0, import_node_path15.join)((0, import_node_os7.homedir)(), ".local", "pipx", "venvs", "openviking"));
15113
- for (const root of roots) {
15114
- for (const candidate of await semanticProcessorCandidates(root)) {
15115
- if ((0, import_node_fs8.existsSync)(candidate)) {
15116
- return candidate;
15117
- }
15118
- }
15119
- }
15120
- if (server) {
15121
- const venvPython = (0, import_node_path15.join)((0, import_node_path15.dirname)(server), process.platform === "win32" ? "python.exe" : "python3");
15122
- if ((0, import_node_fs8.existsSync)(venvPython)) {
15123
- const result = await runCommand(
15124
- venvPython,
15125
- [
15126
- "-c",
15127
- 'import openviking, os; print(os.path.join(os.path.dirname(openviking.__file__), "storage", "queuefs", "semantic_processor.py"))'
15128
- ],
15129
- { allowFailure: true }
15130
- );
15131
- const path2 = result.stdout.trim();
15132
- if (result.exitCode === 0 && path2 && (0, import_node_fs8.existsSync)(path2)) {
15133
- return path2;
15134
- }
15135
- }
15136
- }
15137
- return void 0;
15138
- }
15139
- async function assertPatchedSourceCompiles(source) {
15140
- const python = await findExecutable(["python3", "python"]);
15141
- if (!python) {
15142
- console.warn("WARN python3 not found; skipping compile validation of the patched OpenViking source.");
15143
- return;
15144
- }
15145
- const dir = await (0, import_promises14.mkdtemp)((0, import_node_path15.join)((0, import_node_os7.tmpdir)(), "threadnote-ov-patch-"));
15146
- const file = (0, import_node_path15.join)(dir, "semantic_processor_patched.py");
15147
- try {
15148
- await (0, import_promises14.writeFile)(file, source, "utf8");
15149
- const result = await runCommand(python, ["-m", "py_compile", file], { allowFailure: true });
15150
- if (result.exitCode !== 0) {
15151
- throw new Error(`patched OpenViking source failed to compile: ${result.stderr.trim() || result.stdout.trim()}`);
15152
- }
15153
- } finally {
15154
- await (0, import_promises14.rm)(dir, { force: true, recursive: true });
15155
- }
15156
- }
15157
- async function runRepairSemanticQueue(config, options) {
15158
- const apply = options.apply === true && options.dryRun !== true;
15159
- const path2 = await locateSemanticProcessorPath();
15160
- if (!path2) {
15161
- console.error(
15162
- `Could not locate the installed OpenViking semantic_processor.py. Set ${ENV_OVERRIDE}=/path/to/openviking/storage/queuefs/semantic_processor.py and retry.`
15163
- );
15164
- process.exitCode = 1;
15165
- return;
15166
- }
15167
- console.log(`OpenViking semantic processor: ${path2}`);
15168
- let original;
15169
- try {
15170
- original = await (0, import_promises14.readFile)(path2, "utf8");
15171
- } catch (err) {
15172
- console.error(`Could not read ${path2}: ${errorMessage(err)}`);
15173
- process.exitCode = 1;
15174
- return;
15175
- }
15176
- const result = patchSemanticProcessorSource(original);
15177
- if (result.status === "already-fixed") {
15178
- console.log("OpenViking already guards non-directory memory URIs (#2734/#2735); nothing to patch.");
15179
- return;
15180
- }
15181
- if (result.status === "no-anchor") {
15182
- console.warn(
15183
- "WARN Could not find the _process_memory_directory ls(dir_uri) anchor; this OpenViking layout is unexpected, so no changes were made."
15184
- );
15185
- return;
15186
- }
15187
- if (!apply) {
15188
- console.log("Dry run: would patch OpenViking to skip non-directory/missing memory URIs, then restart the server.");
15189
- console.log("Re-run with --apply to perform it.");
15190
- return;
15191
- }
15192
- await assertPatchedSourceCompiles(result.source);
15193
- const backup = `${path2}.threadnote-bak`;
15194
- if (!(0, import_node_fs8.existsSync)(backup)) {
15195
- await (0, import_promises14.copyFile)(path2, backup);
15196
- }
15197
- await (0, import_promises14.writeFile)(path2, result.source, "utf8");
15198
- console.log(`Patched OpenViking semantic processor (backup: ${backup}).`);
15199
- console.log("Restarting the OpenViking server so the patch loads and any stuck message drains...");
15200
- await runStop(config, {});
15201
- await runStart(config, {});
15202
- console.log("Done. A stuck semantic message drains on the next dequeue; verify with: ov observer queue");
15203
- }
15204
-
15205
15012
  // src/version_command.ts
15206
15013
  async function runVersion(config, options) {
15207
15014
  const currentVersion = await currentPackageVersion();
@@ -15245,9 +15052,9 @@ function printWhatsNew(whatsNew) {
15245
15052
  // src/manager.ts
15246
15053
  var import_node_http2 = require("node:http");
15247
15054
  var import_node_crypto2 = require("node:crypto");
15248
- var import_promises15 = require("node:fs/promises");
15249
- var import_node_os8 = require("node:os");
15250
- var import_node_path16 = require("node:path");
15055
+ var import_promises14 = require("node:fs/promises");
15056
+ var import_node_os7 = require("node:os");
15057
+ var import_node_path15 = require("node:path");
15251
15058
  var STATIC_FILES = {
15252
15059
  "/": { contentType: "text/html; charset=utf-8", path: "index.html" },
15253
15060
  "/index.html": { contentType: "text/html; charset=utf-8", path: "index.html" },
@@ -15318,18 +15125,18 @@ async function readManagedMemory(config, uri) {
15318
15125
  if (!path2) {
15319
15126
  throw new Error(`Manager can only read current-user memory URIs: ${uri}`);
15320
15127
  }
15321
- const [content, pathStat] = await Promise.all([(0, import_promises15.readFile)(path2, "utf8"), (0, import_promises15.stat)(path2)]);
15322
- const relativePath = (0, import_node_path16.relative)(localMemoriesRoot(config), path2).split(import_node_path16.sep).join("/");
15128
+ const [content, pathStat] = await Promise.all([(0, import_promises14.readFile)(path2, "utf8"), (0, import_promises14.stat)(path2)]);
15129
+ const relativePath = (0, import_node_path15.relative)(localMemoriesRoot(config), path2).split(import_node_path15.sep).join("/");
15323
15130
  const record = parseMemoryDocument(uri, content);
15324
15131
  return {
15325
15132
  content,
15326
15133
  node: {
15327
15134
  isDir: false,
15328
15135
  isShared: isInSharedNamespace(config, uri),
15329
- isSystem: isSystemMemoryName(path2.split(import_node_path16.sep).at(-1) ?? ""),
15136
+ isSystem: isSystemMemoryName(path2.split(import_node_path15.sep).at(-1) ?? ""),
15330
15137
  metadata: record?.metadata,
15331
15138
  modTime: pathStat.mtime.toISOString(),
15332
- name: path2.split(import_node_path16.sep).at(-1) ?? uri,
15139
+ name: path2.split(import_node_path15.sep).at(-1) ?? uri,
15333
15140
  relativePath,
15334
15141
  sharedTeam: sharedTeamNameForUri(config, uri),
15335
15142
  size: pathStat.size,
@@ -15617,7 +15424,7 @@ async function handleRequest(context, request, response) {
15617
15424
  }
15618
15425
  async function serveStatic(context, url, response) {
15619
15426
  const file = STATIC_FILES[url.pathname] ?? STATIC_FILES["/"];
15620
- const content = await (0, import_promises15.readFile)((0, import_node_path16.join)(toolRoot(), file.root ?? "manager", file.path));
15427
+ const content = await (0, import_promises14.readFile)((0, import_node_path15.join)(toolRoot(), file.root ?? "manager", file.path));
15621
15428
  const headers = { "content-type": file.contentType };
15622
15429
  if (file.root !== "docs") {
15623
15430
  headers["cache-control"] = "no-store";
@@ -15626,11 +15433,11 @@ async function serveStatic(context, url, response) {
15626
15433
  response.end(content);
15627
15434
  }
15628
15435
  async function readTree(config, path2, uri, relativePath, options = {}) {
15629
- const pathStat = await (0, import_promises15.stat)(path2);
15436
+ const pathStat = await (0, import_promises14.stat)(path2);
15630
15437
  const name = relativePath ? relativePath.split("/").at(-1) ?? relativePath : options.rootName ?? "memories";
15631
15438
  const isDir = pathStat.isDirectory();
15632
15439
  if (!isDir) {
15633
- const record = options.parseMemoryDocuments === false ? void 0 : parseMemoryDocument(uri, await (0, import_promises15.readFile)(path2, "utf8").catch(() => ""));
15440
+ const record = options.parseMemoryDocuments === false ? void 0 : parseMemoryDocument(uri, await (0, import_promises14.readFile)(path2, "utf8").catch(() => ""));
15634
15441
  return {
15635
15442
  isDir: false,
15636
15443
  isShared: isInSharedNamespace(config, uri),
@@ -15644,13 +15451,13 @@ async function readTree(config, path2, uri, relativePath, options = {}) {
15644
15451
  uri
15645
15452
  };
15646
15453
  }
15647
- const entries = await (0, import_promises15.readdir)(path2, { withFileTypes: true });
15454
+ const entries = await (0, import_promises14.readdir)(path2, { withFileTypes: true });
15648
15455
  const children = await Promise.all(
15649
15456
  entries.sort(
15650
15457
  (left, right) => Number(right.isDirectory()) - Number(left.isDirectory()) || left.name.localeCompare(right.name)
15651
15458
  ).map((entry) => {
15652
15459
  const childRelative = relativePath ? `${relativePath}/${entry.name}` : entry.name;
15653
- return readTree(config, (0, import_node_path16.join)(path2, entry.name), `${uri}/${entry.name}`, childRelative, options);
15460
+ return readTree(config, (0, import_node_path15.join)(path2, entry.name), `${uri}/${entry.name}`, childRelative, options);
15654
15461
  })
15655
15462
  );
15656
15463
  return {
@@ -15824,27 +15631,27 @@ async function removeManagedFolder(config, uri) {
15824
15631
  if (!path2) {
15825
15632
  throw new Error(`Manager can only remove current-user memory folders: ${uri}`);
15826
15633
  }
15827
- const pathStat = await (0, import_promises15.stat)(path2);
15634
+ const pathStat = await (0, import_promises14.stat)(path2);
15828
15635
  if (!pathStat.isDirectory()) {
15829
15636
  throw new Error(`Not a folder: ${uri}`);
15830
15637
  }
15831
- const relativePath = (0, import_node_path16.relative)(localMemoriesRoot(config), path2);
15832
- if (!relativePath || relativePath.startsWith("..") || relativePath.split(import_node_path16.sep).includes("..")) {
15638
+ const relativePath = (0, import_node_path15.relative)(localMemoriesRoot(config), path2);
15639
+ if (!relativePath || relativePath.startsWith("..") || relativePath.split(import_node_path15.sep).includes("..")) {
15833
15640
  throw new Error("Refusing to remove a folder outside the memories tree.");
15834
15641
  }
15835
15642
  const fileUris = await fileUrisUnderFolder(config, path2);
15836
15643
  for (const fileUri of fileUris) {
15837
15644
  await runForget(config, fileUri, {});
15838
15645
  }
15839
- await (0, import_promises15.rm)(path2, { force: true, recursive: true });
15646
+ await (0, import_promises14.rm)(path2, { force: true, recursive: true });
15840
15647
  console.log(`Removed folder: ${uri}`);
15841
15648
  console.log(`Forgot ${fileUris.length} file${fileUris.length === 1 ? "" : "s"}.`);
15842
15649
  }
15843
15650
  async function fileUrisUnderFolder(config, folderPath) {
15844
- const entries = await (0, import_promises15.readdir)(folderPath, { withFileTypes: true });
15651
+ const entries = await (0, import_promises14.readdir)(folderPath, { withFileTypes: true });
15845
15652
  const uris = [];
15846
15653
  for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
15847
- const path2 = (0, import_node_path16.join)(folderPath, entry.name);
15654
+ const path2 = (0, import_node_path15.join)(folderPath, entry.name);
15848
15655
  if (entry.isDirectory()) {
15849
15656
  uris.push(...await fileUrisUnderFolder(config, path2));
15850
15657
  } else if (entry.isFile()) {
@@ -15942,11 +15749,11 @@ async function runConsolidationAgent(agent, sources) {
15942
15749
  throw new Error(`${agent} executable was not found.`);
15943
15750
  }
15944
15751
  const prompt = consolidationPrompt(sources);
15945
- const stagingDir = await (0, import_promises15.mkdtemp)((0, import_node_path16.join)((0, import_node_os8.tmpdir)(), "threadnote-consolidate-"));
15946
- const promptPath = (0, import_node_path16.join)(stagingDir, "prompt.txt");
15752
+ const stagingDir = await (0, import_promises14.mkdtemp)((0, import_node_path15.join)((0, import_node_os7.tmpdir)(), "threadnote-consolidate-"));
15753
+ const promptPath = (0, import_node_path15.join)(stagingDir, "prompt.txt");
15947
15754
  try {
15948
- await (0, import_promises15.writeFile)(promptPath, prompt, { encoding: "utf8", mode: 384 });
15949
- await (0, import_promises15.chmod)(promptPath, 384);
15755
+ await (0, import_promises14.writeFile)(promptPath, prompt, { encoding: "utf8", mode: 384 });
15756
+ await (0, import_promises14.chmod)(promptPath, 384);
15950
15757
  const script = consolidationAgentScript(agent, executable);
15951
15758
  const result = await runCommand("sh", ["-lc", script, "threadnote-consolidate", promptPath], {
15952
15759
  allowFailure: true,
@@ -15962,7 +15769,7 @@ async function runConsolidationAgent(agent, sources) {
15962
15769
  }
15963
15770
  return draft;
15964
15771
  } finally {
15965
- await (0, import_promises15.rm)(stagingDir, { force: true, recursive: true });
15772
+ await (0, import_promises14.rm)(stagingDir, { force: true, recursive: true });
15966
15773
  }
15967
15774
  }
15968
15775
  function consolidationAgentScript(agent, executable) {
@@ -16100,10 +15907,10 @@ function memoryDirectoryUri2(config, kind, status, projectSegment) {
16100
15907
  }
16101
15908
  }
16102
15909
  function localMemoriesRoot(config) {
16103
- return (0, import_node_path16.join)(config.agentContextHome, "data", "viking", config.account, "user", uriSegment(config.user), "memories");
15910
+ return (0, import_node_path15.join)(config.agentContextHome, "data", "viking", config.account, "user", uriSegment(config.user), "memories");
16104
15911
  }
16105
15912
  function localResourcesRoot(config) {
16106
- return (0, import_node_path16.join)(config.agentContextHome, "data", "viking", config.account, "resources");
15913
+ return (0, import_node_path15.join)(config.agentContextHome, "data", "viking", config.account, "resources");
16107
15914
  }
16108
15915
  function localPathForMemoryUri(config, uri) {
16109
15916
  const prefix = `viking://user/${uriSegment(config.user)}/memories`;
@@ -16115,17 +15922,17 @@ function localPathForMemoryUri(config, uri) {
16115
15922
  if (segments.some((segment) => segment === "." || segment === "..")) {
16116
15923
  return void 0;
16117
15924
  }
16118
- return (0, import_node_path16.join)(localMemoriesRoot(config), ...segments);
15925
+ return (0, import_node_path15.join)(localMemoriesRoot(config), ...segments);
16119
15926
  }
16120
15927
  function isMissingPathError(err) {
16121
15928
  return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
16122
15929
  }
16123
15930
  function localPathToMemoryUri(config, path2) {
16124
- const relativePath = (0, import_node_path16.relative)(localMemoriesRoot(config), path2);
16125
- if (!relativePath || relativePath.startsWith("..") || relativePath.split(import_node_path16.sep).includes("..")) {
15931
+ const relativePath = (0, import_node_path15.relative)(localMemoriesRoot(config), path2);
15932
+ if (!relativePath || relativePath.startsWith("..") || relativePath.split(import_node_path15.sep).includes("..")) {
16126
15933
  throw new Error(`Path is outside the memories tree: ${path2}`);
16127
15934
  }
16128
- return `viking://user/${uriSegment(config.user)}/memories/${relativePath.split(import_node_path16.sep).join("/")}`;
15935
+ return `viking://user/${uriSegment(config.user)}/memories/${relativePath.split(import_node_path15.sep).join("/")}`;
16129
15936
  }
16130
15937
  async function ensurePersonalDirectoryChain2(config, ov, directoryUri) {
16131
15938
  const prefix = "viking://";
@@ -16365,9 +16172,6 @@ async function main() {
16365
16172
  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) => {
16366
16173
  await runMigrateLifecycle(getRuntimeConfig(program2), options);
16367
16174
  });
16368
- program2.command("repair-semantic-queue").description("Patch the installed OpenViking to drain/avoid the semantic-queue poison loop (#2734)").option("--apply", "Apply the patch and restart the server; without this, prints a dry run").option("--dry-run", "Print what would change without patching or restarting").action(async (options) => {
16369
- await runRepairSemanticQueue(getRuntimeConfig(program2), options);
16370
- });
16371
16175
  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) => {
16372
16176
  await runRecall(getRuntimeConfig(program2), options);
16373
16177
  });
package/docs/index.html CHANGED
@@ -1147,7 +1147,7 @@
1147
1147
  <span class="out">--user`, which fails on PEP 668 setups.</span>
1148
1148
  <span class="out">Install uv now? [Y/n] </span><span class="out-strong">Y</span>
1149
1149
  <span class="out">Installing uv via Homebrew…</span>
1150
- <span class="out-strong">OK openviking 0.4.4 ready · server healthy</span></pre>
1150
+ <span class="out-strong">OK openviking 0.4.5 ready · server healthy</span></pre>
1151
1151
  </div>
1152
1152
  <p class="muted" style="margin-top: 0.75rem; font-size: 0.9rem">
1153
1153
  Or manually: <code>npm install -g threadnote && threadnote install</code>. On a fresh macOS / modern
@@ -1651,7 +1651,7 @@ threadnote doctor --dry-run</code></pre>
1651
1651
  </div>
1652
1652
 
1653
1653
  <p class="colophon">
1654
- threadnote · AGPL-3.0-or-later · built on OpenViking 0.4.4 · use <span class="kbd">↑</span>
1654
+ threadnote · AGPL-3.0-or-later · built on OpenViking 0.4.5 · use <span class="kbd">↑</span>
1655
1655
  <span class="kbd">↓</span> / <span class="kbd">j</span> <span class="kbd">k</span> to navigate
1656
1656
  </p>
1657
1657
  </div>
@@ -102,28 +102,26 @@ Symptom: agents hang or `remember`/`handoff` get very slow, and `~/.openviking/l
102
102
  RuntimeError: Failed to list memory directory viking://user/.../memories/.../<name>.md: Directory not found
103
103
  ```
104
104
 
105
- A memory _file_ got enqueued for directory-level semantic processing; OpenViking's `_process_memory_directory` lists
106
- it, fails, and the message re-enqueues forever. The entry is AGFS-persisted, so it survives a server restart. Check the
107
- queue — a non-zero `Errors`/`Requeued` on the `Semantic` row is the signature:
105
+ A memory _file_ got enqueued for directory-level semantic processing; older OpenViking releases listed it as a
106
+ directory, failed, and re-enqueued the message forever. The entry is AGFS-persisted, so it survives a server restart.
107
+ Check the queue — a non-zero `Errors`/`Requeued` on the `Semantic` row is the signature:
108
108
 
109
109
  ```bash
110
110
  ov observer queue
111
111
  ```
112
112
 
113
- Fix it by patching the installed OpenViking and restarting the server:
113
+ This is fixed upstream in OpenViking 0.4.5. Update Threadnote so it upgrades the pinned OpenViking install and restarts
114
+ the server:
114
115
 
115
116
  ```bash
116
- threadnote repair-semantic-queue --apply
117
+ threadnote update
117
118
  ```
118
119
 
119
- It skips non-directory/missing memory URIs (OpenViking PR #2735), keeps a `.threadnote-bak`, compile-checks the patched
120
- file before writing, and is a no-op once the installed OpenViking already includes the fix. This is a **temporary local
121
- patch** — `threadnote update` also offers it as a post-update step, and it is superseded automatically once Threadnote
122
- pins an OpenViking release containing the fix. To revert manually:
120
+ If Threadnote is already current but OpenViking is still older than 0.4.5, force a reinstall of the pinned OpenViking
121
+ tool:
123
122
 
124
123
  ```bash
125
- mv <printed-path>.threadnote-bak <printed-path>
126
- threadnote stop && threadnote start
124
+ threadnote install --force
127
125
  ```
128
126
 
129
127
  ## Port Already In Use
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "threadnote",
3
- "version": "1.4.3",
3
+ "version": "1.4.4",
4
4
  "type": "commonjs",
5
5
  "main": "dist/threadnote.cjs",
6
6
  "description": "Shared local context and handoffs for development agents",