threadnote 1.3.3 → 1.4.1

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.
@@ -35689,6 +35689,9 @@ function commandResultFromExecFileCallback(params) {
35689
35689
  function defaultCommandTimeoutMs() {
35690
35690
  return positiveIntegerFromEnv("THREADNOTE_COMMAND_TIMEOUT_MS") ?? 10 * 60 * 1e3;
35691
35691
  }
35692
+ function reindexWaitTimeoutMs() {
35693
+ return positiveIntegerFromEnv("THREADNOTE_REINDEX_TIMEOUT_MS") ?? 12e4;
35694
+ }
35692
35695
  function defaultCommandMaxOutputBytes() {
35693
35696
  return positiveIntegerFromEnv("THREADNOTE_COMMAND_MAX_OUTPUT_BYTES") ?? 5 * 1024 * 1024;
35694
35697
  }
@@ -36290,7 +36293,7 @@ function readStringArray(object3, key) {
36290
36293
 
36291
36294
  // src/runtime.ts
36292
36295
  function withIdentity(config2, args) {
36293
- return [...args, "--account", config2.account, "--user", config2.user, "--agent-id", config2.agentId];
36296
+ return [...args, "--account", config2.account, "--user", config2.user];
36294
36297
  }
36295
36298
 
36296
36299
  // src/index_repair.ts
@@ -36334,7 +36337,11 @@ async function repairStaleRecallIndex(config2, ov, options = {}) {
36334
36337
  const result = await runCommand(
36335
36338
  ov,
36336
36339
  withIdentity(config2, ["reindex", target.uri, "--mode", "semantic_and_vectors", "--wait", "true"]),
36337
- { allowFailure: true }
36340
+ // Bound the wait: `ov reindex` has no --timeout, so a stuck/poisoned
36341
+ // semantic queue would block each target for the full 10-min command
36342
+ // timeout. A timed-out target counts as a failure and trips the
36343
+ // consecutive-failure stop instead of hanging the whole repair.
36344
+ { allowFailure: true, timeoutMs: reindexWaitTimeoutMs() }
36338
36345
  );
36339
36346
  if (result.exitCode === 0) {
36340
36347
  consecutiveFailures = 0;
@@ -38580,7 +38587,7 @@ async function refreshMemoryIndex(config2, ov, uri, options = {}) {
38580
38587
  const result = await runCommand(
38581
38588
  ov,
38582
38589
  withIdentity(config2, ["reindex", uri, "--mode", "vectors_only", "--wait", "true"]),
38583
- { allowFailure: true }
38590
+ { allowFailure: true, timeoutMs: reindexWaitTimeoutMs() }
38584
38591
  );
38585
38592
  if (result.exitCode === 0) {
38586
38593
  if (options.quiet !== true && result.stdout.trim()) {
@@ -39359,7 +39366,7 @@ function registerTools(server, config2) {
39359
39366
  }
39360
39367
  },
39361
39368
  async ({ recursive, uri }) => {
39362
- const checkedUri = requiredVikingUri(uri, "forget", "viking://agent/threadnote/memories/example.md");
39369
+ const checkedUri = requiredVikingUri(uri, "forget", "viking://user/you/memories/example.md");
39363
39370
  if (!checkedUri.ok) {
39364
39371
  return checkedUri.error;
39365
39372
  }
@@ -39603,8 +39610,6 @@ function registerOpenVikingParityTools(server, config2) {
39603
39610
  limit: external_exports.number().int().positive().max(1e3).optional().describe("Maximum result count"),
39604
39611
  minScore: external_exports.number().min(0).max(1).optional().describe("Minimum score threshold"),
39605
39612
  min_score: external_exports.number().min(0).max(1).optional().describe("Minimum score threshold"),
39606
- peerId: external_exports.string().optional().describe("Optional native peer id"),
39607
- peer_id: external_exports.string().optional().describe("Optional native peer id"),
39608
39613
  query: external_exports.string().optional().describe("Required search query"),
39609
39614
  sessionId: external_exports.string().optional().describe("Optional native session id"),
39610
39615
  session_id: external_exports.string().optional().describe("Optional native session id"),
@@ -40081,7 +40086,7 @@ async function collectExactMemoryMatches(config2, query, includeArchived, projec
40081
40086
  return collectExactMatches(terms, scopes, async (term, scope) => {
40082
40087
  const result = await runCommand(
40083
40088
  ov,
40084
- withIdentity2(config2, ["grep", term, "--uri", scope, "--node-limit", "5", "--output", "json"]),
40089
+ withIdentity(config2, ["grep", term, "--uri", scope, "--node-limit", "5", "--output", "json"]),
40085
40090
  { allowFailure: true }
40086
40091
  );
40087
40092
  return result.exitCode === 0 ? result.stdout : void 0;
@@ -40099,7 +40104,7 @@ function registerReadTool(server, config2, name, description) {
40099
40104
  }
40100
40105
  },
40101
40106
  async ({ uri, uris }) => {
40102
- const checkedUris = requiredVikingUriList(uris ?? uri, name, "viking://agent/threadnote/memories/.abstract.md");
40107
+ const checkedUris = requiredVikingUriList(uris ?? uri, name, "viking://user/you/memories/.abstract.md");
40103
40108
  if (!checkedUris.ok) {
40104
40109
  return checkedUris.error;
40105
40110
  }
@@ -40526,11 +40531,11 @@ async function writeSharedMemoryReplacement(config2, ov, params, targetUri) {
40526
40531
  return { content: [{ type: "text", text: messages.join("\n") }] };
40527
40532
  }
40528
40533
  async function vikingResourceExists2(ov, config2, uri) {
40529
- const stat2 = await runCommand(ov, withIdentity2(config2, ["stat", uri]), { allowFailure: true });
40534
+ const stat2 = await runCommand(ov, withIdentity(config2, ["stat", uri]), { allowFailure: true });
40530
40535
  return stat2.exitCode === 0;
40531
40536
  }
40532
40537
  async function removeVikingResourceWithRetry(ov, config2, uri, recursive = false) {
40533
- const args = withIdentity2(config2, ["rm", uri, ...recursive ? ["--recursive"] : []]);
40538
+ const args = withIdentity(config2, ["rm", uri, ...recursive ? ["--recursive"] : []]);
40534
40539
  for (let attempt = 0; attempt < 4; attempt += 1) {
40535
40540
  const result = await runCommand(ov, args, { allowFailure: true });
40536
40541
  if (result.exitCode === 0) {
@@ -40570,13 +40575,13 @@ ${stdout2}`.toLowerCase();
40570
40575
  }
40571
40576
  async function ensureMemoryDirectory(ov, config2, directoryUri) {
40572
40577
  for (const uri of vikingDirectoryChain(directoryUri)) {
40573
- const statResult = await runCommand(ov, withIdentity2(config2, ["stat", uri]), { allowFailure: true });
40578
+ const statResult = await runCommand(ov, withIdentity(config2, ["stat", uri]), { allowFailure: true });
40574
40579
  if (statResult.exitCode === 0) {
40575
40580
  continue;
40576
40581
  }
40577
40582
  await runCommand(
40578
40583
  ov,
40579
- withIdentity2(config2, ["mkdir", uri, "--description", "Threadnote lifecycle-aware local memories."])
40584
+ withIdentity(config2, ["mkdir", uri, "--description", "Threadnote lifecycle-aware local memories."])
40580
40585
  );
40581
40586
  }
40582
40587
  }
@@ -40793,7 +40798,7 @@ ${text}`);
40793
40798
  async function runOpenVikingCliReadTool(config2, uri) {
40794
40799
  try {
40795
40800
  const ov = await requiredOpenVikingCli2();
40796
- const result = await runCommand(ov, withIdentity2(config2, ["read", uri]));
40801
+ const result = await runCommand(ov, withIdentity(config2, ["read", uri]));
40797
40802
  const text = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n");
40798
40803
  return { content: [{ type: "text", text: text || "OK" }] };
40799
40804
  } catch (err) {
@@ -40803,7 +40808,7 @@ async function runOpenVikingCliReadTool(config2, uri) {
40803
40808
  async function runOpenVikingTool(config2, args) {
40804
40809
  try {
40805
40810
  const ov = await requiredOpenVikingCli2();
40806
- const result = await runCommand(ov, withIdentity2(config2, args));
40811
+ const result = await runCommand(ov, withIdentity(config2, args));
40807
40812
  const text = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n");
40808
40813
  return { content: [{ type: "text", text: text || "OK" }] };
40809
40814
  } catch (err) {
@@ -40816,8 +40821,9 @@ async function runOpenVikingMcpTool(config2, toolName, args) {
40816
40821
  const transport = new StreamableHTTPClientTransport(new URL(config2.openVikingMcpUrl), {
40817
40822
  requestInit: {
40818
40823
  headers: {
40824
+ // OpenViking 0.4.x dropped agent_id as an identity input; only
40825
+ // account + user are honored (mirrors withIdentity in runtime.ts).
40819
40826
  "X-OpenViking-Account": config2.account,
40820
- "X-OpenViking-Agent": config2.agentId,
40821
40827
  "X-OpenViking-User": config2.user
40822
40828
  }
40823
40829
  }
@@ -41048,9 +41054,6 @@ function shareArtifactToolHeader(team, syncedTeams, warnings) {
41048
41054
  }
41049
41055
  return lines;
41050
41056
  }
41051
- function withIdentity2(config2, args) {
41052
- return [...args, "--account", config2.account, "--user", config2.user, "--agent-id", config2.agentId];
41053
- }
41054
41057
  async function requiredOpenVikingCli2() {
41055
41058
  const command2 = await findOpenVikingCli();
41056
41059
  if (!command2) {
@@ -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.3.24";
3397
+ var DEFAULT_OPENVIKING_VERSION = "0.4.4";
3398
3398
  var OPENVIKING_TOOL_PYTHON = "3.12";
3399
3399
  var DEFAULT_ACCOUNT = "local";
3400
3400
  var DEFAULT_AGENT_ID = "threadnote";
@@ -3412,6 +3412,7 @@ var USER_INSTRUCTIONS_START_MARKER = "<!-- BEGIN THREADNOTE USER INSTRUCTIONS --
3412
3412
  var USER_INSTRUCTIONS_END_MARKER = "<!-- END THREADNOTE USER INSTRUCTIONS -->";
3413
3413
  var USER_MANIFEST_NAME = "seed-manifest.yaml";
3414
3414
  var SEED_STATE_FILE = "seed-state.json";
3415
+ var SEED_WATCH_INTERVAL_ENV = "THREADNOTE_SEED_WATCH_INTERVAL";
3415
3416
  var USER_AGENT_INSTRUCTION_TARGETS = [
3416
3417
  { kind: "block", label: "codex user instructions", path: "~/.codex/AGENTS.md" },
3417
3418
  { kind: "block", label: "claude user instructions", path: "~/.claude/CLAUDE.md" },
@@ -3834,6 +3835,9 @@ function commandResultFromExecFileCallback(params) {
3834
3835
  function defaultCommandTimeoutMs() {
3835
3836
  return positiveIntegerFromEnv("THREADNOTE_COMMAND_TIMEOUT_MS") ?? 10 * 60 * 1e3;
3836
3837
  }
3838
+ function reindexWaitTimeoutMs() {
3839
+ return positiveIntegerFromEnv("THREADNOTE_REINDEX_TIMEOUT_MS") ?? 12e4;
3840
+ }
3837
3841
  function defaultCommandMaxOutputBytes() {
3838
3842
  return positiveIntegerFromEnv("THREADNOTE_COMMAND_MAX_OUTPUT_BYTES") ?? 5 * 1024 * 1024;
3839
3843
  }
@@ -3915,24 +3919,34 @@ function compareVersions(a, b) {
3915
3919
  return difference;
3916
3920
  }
3917
3921
  }
3918
- if (left.prerelease === right.prerelease) {
3919
- return 0;
3922
+ const rankDelta = suffixRank(left.suffix) - suffixRank(right.suffix);
3923
+ if (rankDelta !== 0) {
3924
+ return rankDelta;
3920
3925
  }
3921
- if (left.prerelease === void 0) {
3922
- return 1;
3926
+ if (left.suffix === right.suffix) {
3927
+ return 0;
3923
3928
  }
3924
- if (right.prerelease === void 0) {
3925
- return -1;
3929
+ return (left.suffix ?? "").localeCompare(right.suffix ?? "");
3930
+ }
3931
+ function suffixRank(suffix) {
3932
+ if (suffix === void 0) {
3933
+ return 0;
3926
3934
  }
3927
- return left.prerelease.localeCompare(right.prerelease);
3935
+ return /^post/i.test(suffix) ? 1 : -1;
3928
3936
  }
3929
3937
  function parseVersion(version) {
3930
- const normalized = version.trim().replace(/^v/, "");
3931
- const [core, prerelease] = normalized.split("-", 2);
3932
- const parts = core.split(".").map((part) => Number(part));
3938
+ const normalized = version.trim().replace(/^v/, "").split("+", 1)[0];
3939
+ const core = normalized.match(/^\d+(?:\.\d+){0,2}/)?.[0] ?? "";
3940
+ const rawSuffix = normalized.slice(core.length).replace(/^[-_.]/, "");
3941
+ const suffix = core.length > 0 && rawSuffix.length > 0 ? rawSuffix : void 0;
3942
+ const parts = core.split(".");
3933
3943
  return {
3934
- numbers: [safeVersionNumber(parts[0]), safeVersionNumber(parts[1]), safeVersionNumber(parts[2])],
3935
- prerelease
3944
+ numbers: [
3945
+ safeVersionNumber(Number.parseInt(parts[0] ?? "", 10)),
3946
+ safeVersionNumber(Number.parseInt(parts[1] ?? "", 10)),
3947
+ safeVersionNumber(Number.parseInt(parts[2] ?? "", 10))
3948
+ ],
3949
+ suffix
3936
3950
  };
3937
3951
  }
3938
3952
  function safeVersionNumber(value) {
@@ -7537,7 +7551,7 @@ function openVikingServerArgs(config) {
7537
7551
  return ["--config", (0, import_node_path4.join)(config.agentContextHome, "ov.conf"), "--host", config.host, "--port", String(config.port)];
7538
7552
  }
7539
7553
  function withIdentity(config, args) {
7540
- return [...args, "--account", config.account, "--user", config.user, "--agent-id", config.agentId];
7554
+ return [...args, "--account", config.account, "--user", config.user];
7541
7555
  }
7542
7556
  function renderTemplate(template, config, extras = {}) {
7543
7557
  let rendered = template.replaceAll("{{THREADNOTE_HOME}}", config.agentContextHome).replaceAll("{{OPENVIKING_ACCOUNT}}", config.account).replaceAll("{{OPENVIKING_AGENT_ID}}", config.agentId).replaceAll("{{OPENVIKING_HOST}}", config.host).replaceAll("{{OPENVIKING_PORT}}", String(config.port)).replaceAll("{{OPENVIKING_USER}}", config.user);
@@ -7591,7 +7605,11 @@ async function repairStaleRecallIndex(config, ov, options = {}) {
7591
7605
  const result = await runCommand(
7592
7606
  ov,
7593
7607
  withIdentity(config, ["reindex", target.uri, "--mode", "semantic_and_vectors", "--wait", "true"]),
7594
- { allowFailure: true }
7608
+ // Bound the wait: `ov reindex` has no --timeout, so a stuck/poisoned
7609
+ // semantic queue would block each target for the full 10-min command
7610
+ // timeout. A timed-out target counts as a failure and trips the
7611
+ // consecutive-failure stop instead of hanging the whole repair.
7612
+ { allowFailure: true, timeoutMs: reindexWaitTimeoutMs() }
7595
7613
  );
7596
7614
  if (result.exitCode === 0) {
7597
7615
  consecutiveFailures = 0;
@@ -10894,7 +10912,7 @@ async function refreshMemoryIndex(config, ov, uri, options = {}) {
10894
10912
  const result = await runCommand(
10895
10913
  ov,
10896
10914
  withIdentity(config, ["reindex", uri, "--mode", "vectors_only", "--wait", "true"]),
10897
- { allowFailure: true }
10915
+ { allowFailure: true, timeoutMs: reindexWaitTimeoutMs() }
10898
10916
  );
10899
10917
  if (result.exitCode === 0) {
10900
10918
  if (options.quiet !== true && result.stdout.trim()) {
@@ -12254,7 +12272,7 @@ async function checkForThreadnoteUpdate(args) {
12254
12272
  }
12255
12273
  const cached = await readUpdateCache(args.cachePath);
12256
12274
  if (cached && isCacheFresh(cached)) {
12257
- return compareVersions2(args.currentVersion, cached.latestVersion);
12275
+ return toUpdateCheckResult(args.currentVersion, cached.latestVersion);
12258
12276
  }
12259
12277
  const fresh = await fetchLatestVersion();
12260
12278
  if (fresh) {
@@ -12263,10 +12281,10 @@ async function checkForThreadnoteUpdate(args) {
12263
12281
  latestVersion: fresh,
12264
12282
  version: 1
12265
12283
  });
12266
- return compareVersions2(args.currentVersion, fresh);
12284
+ return toUpdateCheckResult(args.currentVersion, fresh);
12267
12285
  }
12268
12286
  if (cached) {
12269
- return compareVersions2(args.currentVersion, cached.latestVersion);
12287
+ return toUpdateCheckResult(args.currentVersion, cached.latestVersion);
12270
12288
  }
12271
12289
  return void 0;
12272
12290
  }
@@ -12284,27 +12302,13 @@ function spawnDetachedAutoUpdate() {
12284
12302
  } catch {
12285
12303
  }
12286
12304
  }
12287
- function compareVersions2(currentVersion, latestVersion) {
12305
+ function toUpdateCheckResult(currentVersion, latestVersion) {
12288
12306
  return {
12289
12307
  currentVersion,
12290
12308
  latestVersion,
12291
- outdated: isNewerVersion(latestVersion, currentVersion)
12309
+ outdated: compareVersions(latestVersion, currentVersion) > 0
12292
12310
  };
12293
12311
  }
12294
- function isNewerVersion(candidate, baseline) {
12295
- const candidateParts = parseVersion2(candidate);
12296
- const baselineParts = parseVersion2(baseline);
12297
- for (let index = 0; index < 3; index += 1) {
12298
- if (candidateParts[index] !== baselineParts[index]) {
12299
- return candidateParts[index] > baselineParts[index];
12300
- }
12301
- }
12302
- return false;
12303
- }
12304
- function parseVersion2(value) {
12305
- const parts = value.split(".").map((part) => parseInt(part, 10));
12306
- return [parts[0] || 0, parts[1] || 0, parts[2] || 0];
12307
- }
12308
12312
  function isCacheFresh(cache) {
12309
12313
  const checkedAt = new Date(cache.checkedAt).getTime();
12310
12314
  return Number.isFinite(checkedAt) && Date.now() - checkedAt < CACHE_TTL_MS;
@@ -12584,10 +12588,24 @@ async function emitUpdateBannerIfOutdated(config) {
12584
12588
  // src/seeding.ts
12585
12589
  var import_promises9 = require("node:fs/promises");
12586
12590
  var import_node_path12 = require("node:path");
12591
+ function parseSeedWatchIntervalMinutes(rawValue) {
12592
+ if (rawValue === void 0) {
12593
+ return void 0;
12594
+ }
12595
+ const minutes = Number.parseInt(rawValue.trim(), 10);
12596
+ return Number.isInteger(minutes) && minutes > 0 ? minutes : void 0;
12597
+ }
12598
+ function seedWatchArgs(params) {
12599
+ if (params.watchIntervalMinutes === void 0 || !params.importedOriginal || params.redactionProne) {
12600
+ return [];
12601
+ }
12602
+ return ["--watch-interval", String(params.watchIntervalMinutes)];
12603
+ }
12587
12604
  async function runSeed(config, options) {
12588
12605
  const manifest = await readSeedManifest(config.manifestPath);
12589
12606
  const ignorePatterns = await loadIgnorePatterns();
12590
12607
  const ov = await openVikingCliForMode(options.dryRun === true);
12608
+ const watchIntervalMinutes = parseSeedWatchIntervalMinutes(process.env[SEED_WATCH_INTERVAL_ENV]);
12591
12609
  const statePath = (0, import_node_path12.join)(config.agentContextHome, SEED_STATE_FILE);
12592
12610
  const state = options.force === true ? { files: {}, version: 1 } : await readSeedState(statePath);
12593
12611
  const projects = filterProjects(manifest.projects, options.only);
@@ -12620,6 +12638,11 @@ async function runSeed(config, options) {
12620
12638
  candidate.destinationUri,
12621
12639
  "--reason",
12622
12640
  seedResourceReason(candidate),
12641
+ ...seedWatchArgs({
12642
+ watchIntervalMinutes,
12643
+ importedOriginal: importPath === candidate.filePath,
12644
+ redactionProne: shouldRedactPath(candidate.relativePath)
12645
+ }),
12623
12646
  "--wait"
12624
12647
  ]);
12625
12648
  await maybeRun(options.dryRun === true, ov, args);
@@ -13701,6 +13724,8 @@ async function collectDoctorChecks(config, options = {}) {
13701
13724
  checks.push(await commandCheck("python3", ["--version"]));
13702
13725
  checks.push(await openVikingServerCheck());
13703
13726
  checks.push(await openVikingCliCheck());
13727
+ checks.push(await openVikingVersionCheck(config));
13728
+ checks.push(await recallShapeCheck(config));
13704
13729
  checks.push(await localEmbeddingCheck());
13705
13730
  checks.push(await pythonSystemCertificatesCheck());
13706
13731
  checks.push(await firstCommandCheck("python installer", ["pipx", "uv", "pip3"], ["--version"]));
@@ -13962,21 +13987,13 @@ async function configureOpenVikingCliLanguage(config, dryRun) {
13962
13987
  if (!ov) {
13963
13988
  return;
13964
13989
  }
13965
- const installedVersion = dryRun ? void 0 : await readOpenVikingCliVersion2(ov);
13990
+ const installedVersion = dryRun ? void 0 : await readOpenVikingCliVersion(ov);
13966
13991
  const effectiveVersion = installedVersion ?? config.openVikingVersion;
13967
13992
  if (compareVersions(effectiveVersion, "0.3.23") < 0) {
13968
13993
  return;
13969
13994
  }
13970
13995
  await maybeRun(dryRun, ov, ["language", "en"], { allowFailure: true });
13971
13996
  }
13972
- async function readOpenVikingCliVersion2(ov) {
13973
- const result = await runCommand(ov, ["version"], { allowFailure: true });
13974
- if (result.exitCode !== 0) {
13975
- return void 0;
13976
- }
13977
- const match = result.stdout.match(/^\s*CLI:\s*(\S+)/m);
13978
- return match ? match[1] : void 0;
13979
- }
13980
13997
  async function findOpenVikingServer() {
13981
13998
  const onPath = await findExecutable([OPENVIKING_SERVER_COMMAND]);
13982
13999
  if (onPath) {
@@ -14213,6 +14230,59 @@ async function openVikingCliCheck() {
14213
14230
  detail: result.exitCode === 0 ? detail : firstLine(result.stderr || result.stdout) || detail
14214
14231
  };
14215
14232
  }
14233
+ async function openVikingVersionCheck(config) {
14234
+ const executable = await findOpenVikingCli();
14235
+ const pinned = config.openVikingVersion;
14236
+ if (!executable) {
14237
+ return { name: "openviking version", status: "warn", detail: `CLI not found; pinned ${pinned}` };
14238
+ }
14239
+ const installed = await readOpenVikingCliVersion(executable);
14240
+ if (!installed) {
14241
+ return {
14242
+ name: "openviking version",
14243
+ status: "warn",
14244
+ detail: `could not detect via \`${executable} version\`; pinned ${pinned}`
14245
+ };
14246
+ }
14247
+ if (compareVersions(installed, pinned) < 0) {
14248
+ return {
14249
+ name: "openviking version",
14250
+ status: "warn",
14251
+ detail: `installed ${installed} is older than pinned ${pinned}; run \`threadnote repair\` or \`threadnote update\` to upgrade`
14252
+ };
14253
+ }
14254
+ return { name: "openviking version", status: "ok", detail: `${installed} (pinned ${pinned})` };
14255
+ }
14256
+ async function recallShapeCheck(config) {
14257
+ const executable = await findOpenVikingCli();
14258
+ if (!executable) {
14259
+ return { name: "recall shape", status: "warn", detail: "CLI not found" };
14260
+ }
14261
+ const args = withIdentity(config, ["find", "threadnote", "--node-limit", "1", "--output", "json"]);
14262
+ const result = await runCommand(executable, args, { allowFailure: true });
14263
+ if (result.exitCode !== 0) {
14264
+ return { name: "recall shape", status: "warn", detail: "search failed; run threadnote repair" };
14265
+ }
14266
+ let parsed;
14267
+ try {
14268
+ parsed = JSON.parse(result.stdout.trim());
14269
+ } catch {
14270
+ return {
14271
+ name: "recall shape",
14272
+ status: "warn",
14273
+ detail: "search output is not JSON; recall may silently return nothing"
14274
+ };
14275
+ }
14276
+ const buckets = ["memories", "resources", "skills"];
14277
+ if (!isJsonObject(parsed) || !buckets.some((key) => Array.isArray(parsed[key]))) {
14278
+ return {
14279
+ name: "recall shape",
14280
+ status: "warn",
14281
+ detail: `search JSON missing ${buckets.join("/")} buckets; recall parsing is out of sync with this OpenViking`
14282
+ };
14283
+ }
14284
+ return { name: "recall shape", status: "ok", detail: "memories/resources/skills buckets present" };
14285
+ }
14216
14286
  async function localEmbeddingCheck() {
14217
14287
  const serverPath = await findOpenVikingServer();
14218
14288
  if (!serverPath) {
@@ -193,8 +193,8 @@ Use these only when MCP tools are not available:
193
193
  threadnote start
194
194
  threadnote recall --query "last handoff for this branch"
195
195
  threadnote recall --query "durable feature knowledge for this branch"
196
- threadnote read viking://agent/threadnote/memories/.abstract.md
197
- threadnote list viking://agent/threadnote/memories --all --recursive
196
+ threadnote read viking://user/example/memories/.abstract.md
197
+ threadnote list viking://user/example/memories --all --recursive
198
198
  threadnote remember --kind durable --project example --topic workflow --text "Durable engineering note..."
199
199
  threadnote remember --kind durable --project example --topic active-issue --text "Feature knowledge..."
200
200
  threadnote remember --replace viking://user/example/memories/durable/projects/example/workflow.md --text "Updated durable engineering note..."
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.3.24 ready · server healthy</span></pre>
1150
+ <span class="out-strong">OK openviking 0.4.4 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.3.24 · use <span class="kbd">↑</span>
1654
+ threadnote · AGPL-3.0-or-later · built on OpenViking 0.4.4 · 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>
package/docs/migration.md CHANGED
@@ -108,6 +108,12 @@ paths.
108
108
  threadnote seed
109
109
  ```
110
110
 
111
+ Seeded docs refresh on the next `threadnote seed`/`repair`, which re-runs the
112
+ secret scan. To let OpenViking auto-refresh them between runs, opt in with
113
+ `THREADNOTE_SEED_WATCH_INTERVAL=<minutes> threadnote seed`. Watches attach
114
+ only to original, non-redaction-prone files, since an OpenViking-managed
115
+ refresh re-ingests the file without Threadnote's per-import secret scan.
116
+
111
117
  7. Inspect and seed shared skills:
112
118
 
113
119
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "threadnote",
3
- "version": "1.3.3",
3
+ "version": "1.4.1",
4
4
  "type": "commonjs",
5
5
  "main": "dist/threadnote.cjs",
6
6
  "description": "Shared local context and handoffs for development agents",