threadnote 0.7.12 → 1.0.0

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.
@@ -3645,49 +3645,49 @@ var require_fast_uri = __commonJS({
3645
3645
  schemelessOptions.skipEscape = true;
3646
3646
  return serialize(resolved, schemelessOptions);
3647
3647
  }
3648
- function resolveComponent(base, relative2, options, skipNormalization) {
3648
+ function resolveComponent(base, relative3, options, skipNormalization) {
3649
3649
  const target = {};
3650
3650
  if (!skipNormalization) {
3651
3651
  base = parse3(serialize(base, options), options);
3652
- relative2 = parse3(serialize(relative2, options), options);
3652
+ relative3 = parse3(serialize(relative3, options), options);
3653
3653
  }
3654
3654
  options = options || {};
3655
- if (!options.tolerant && relative2.scheme) {
3656
- target.scheme = relative2.scheme;
3657
- target.userinfo = relative2.userinfo;
3658
- target.host = relative2.host;
3659
- target.port = relative2.port;
3660
- target.path = removeDotSegments(relative2.path || "");
3661
- target.query = relative2.query;
3655
+ if (!options.tolerant && relative3.scheme) {
3656
+ target.scheme = relative3.scheme;
3657
+ target.userinfo = relative3.userinfo;
3658
+ target.host = relative3.host;
3659
+ target.port = relative3.port;
3660
+ target.path = removeDotSegments(relative3.path || "");
3661
+ target.query = relative3.query;
3662
3662
  } else {
3663
- if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
3664
- target.userinfo = relative2.userinfo;
3665
- target.host = relative2.host;
3666
- target.port = relative2.port;
3667
- target.path = removeDotSegments(relative2.path || "");
3668
- target.query = relative2.query;
3663
+ if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
3664
+ target.userinfo = relative3.userinfo;
3665
+ target.host = relative3.host;
3666
+ target.port = relative3.port;
3667
+ target.path = removeDotSegments(relative3.path || "");
3668
+ target.query = relative3.query;
3669
3669
  } else {
3670
- if (!relative2.path) {
3670
+ if (!relative3.path) {
3671
3671
  target.path = base.path;
3672
- if (relative2.query !== void 0) {
3673
- target.query = relative2.query;
3672
+ if (relative3.query !== void 0) {
3673
+ target.query = relative3.query;
3674
3674
  } else {
3675
3675
  target.query = base.query;
3676
3676
  }
3677
3677
  } else {
3678
- if (relative2.path[0] === "/") {
3679
- target.path = removeDotSegments(relative2.path);
3678
+ if (relative3.path[0] === "/") {
3679
+ target.path = removeDotSegments(relative3.path);
3680
3680
  } else {
3681
3681
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
3682
- target.path = "/" + relative2.path;
3682
+ target.path = "/" + relative3.path;
3683
3683
  } else if (!base.path) {
3684
- target.path = relative2.path;
3684
+ target.path = relative3.path;
3685
3685
  } else {
3686
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative2.path;
3686
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
3687
3687
  }
3688
3688
  target.path = removeDotSegments(target.path);
3689
3689
  }
3690
- target.query = relative2.query;
3690
+ target.query = relative3.query;
3691
3691
  }
3692
3692
  target.userinfo = base.userinfo;
3693
3693
  target.host = base.host;
@@ -3695,7 +3695,7 @@ var require_fast_uri = __commonJS({
3695
3695
  }
3696
3696
  target.scheme = base.scheme;
3697
3697
  }
3698
- target.fragment = relative2.fragment;
3698
+ target.fragment = relative3.fragment;
3699
3699
  return target;
3700
3700
  }
3701
3701
  function equal(uriA, uriB, options) {
@@ -30950,14 +30950,18 @@ var StdioServerTransport = class {
30950
30950
  };
30951
30951
 
30952
30952
  // src/mcp_server.ts
30953
- var import_promises4 = require("node:fs/promises");
30953
+ var import_promises5 = require("node:fs/promises");
30954
30954
  var import_node_os3 = require("node:os");
30955
- var import_node_path4 = require("node:path");
30955
+ var import_node_path5 = require("node:path");
30956
30956
 
30957
30957
  // src/constants.ts
30958
30958
  var DEFAULT_ACCOUNT = "local";
30959
30959
  var DEFAULT_AGENT_ID = "threadnote";
30960
30960
 
30961
+ // src/index_repair.ts
30962
+ var import_promises3 = require("node:fs/promises");
30963
+ var import_node_path2 = require("node:path");
30964
+
30961
30965
  // src/manifest.ts
30962
30966
  var import_promises2 = require("node:fs/promises");
30963
30967
 
@@ -33808,6 +33812,13 @@ async function sleep(ms) {
33808
33812
  setTimeout(resolvePromise, ms);
33809
33813
  });
33810
33814
  }
33815
+ async function ensureDirectory(path, dryRun) {
33816
+ if (dryRun) {
33817
+ console.log(`Would create directory: ${path}`);
33818
+ return;
33819
+ }
33820
+ await (0, import_promises.mkdir)(path, { recursive: true });
33821
+ }
33811
33822
  async function exists(path) {
33812
33823
  try {
33813
33824
  await (0, import_promises.access)(path);
@@ -33893,6 +33904,9 @@ function uniqueUsefulWorkspaceTerms(values) {
33893
33904
  }
33894
33905
  return terms;
33895
33906
  }
33907
+ function toPosixPath(path) {
33908
+ return path.split(import_node_path.sep).join("/");
33909
+ }
33896
33910
  function trimTrailingSlash(value) {
33897
33911
  return value.endsWith("/") ? value.slice(0, -1) : value;
33898
33912
  }
@@ -34043,8 +34057,236 @@ function readStringArray(object3, key) {
34043
34057
  return value;
34044
34058
  }
34045
34059
 
34060
+ // src/runtime.ts
34061
+ function withIdentity(config2, args) {
34062
+ return [...args, "--account", config2.account, "--user", config2.user, "--agent-id", config2.agentId];
34063
+ }
34064
+
34065
+ // src/index_repair.ts
34066
+ var AUTO_REPAIR_STATE_FILE = "index-auto-repair.json";
34067
+ var AUTO_REPAIR_TTL_MS = 6 * 60 * 60 * 1e3;
34068
+ var MAX_SCAN_DEPTH = 5;
34069
+ var MAX_REPAIR_TARGETS = 4;
34070
+ async function repairStaleRecallIndex(config2, ov, options = {}) {
34071
+ const targets = await findStaleRecallIndexTargets(config2, options);
34072
+ if (targets.length === 0) {
34073
+ return { repairedUris: [], skippedRecentUris: [], warnings: [] };
34074
+ }
34075
+ const state = options.dryRun === true ? { entries: {}, version: 1 } : await readAutoRepairState(config2);
34076
+ const now = Date.now();
34077
+ const repairedUris = [];
34078
+ const skippedRecentUris = [];
34079
+ const warnings = [];
34080
+ for (const target of targets.slice(0, options.maxTargets ?? MAX_REPAIR_TARGETS)) {
34081
+ const previous = state.entries[target.uri];
34082
+ if (previous?.signature === target.signature && now - Date.parse(previous.repairedAt) < AUTO_REPAIR_TTL_MS && options.dryRun !== true && options.ignoreBackoff !== true) {
34083
+ skippedRecentUris.push(target.uri);
34084
+ continue;
34085
+ }
34086
+ if (options.dryRun === true) {
34087
+ repairedUris.push(target.uri);
34088
+ continue;
34089
+ }
34090
+ const result = await runCommand(
34091
+ ov,
34092
+ withIdentity(config2, ["reindex", target.uri, "--mode", "semantic_and_vectors", "--wait", "true"]),
34093
+ { allowFailure: true }
34094
+ );
34095
+ if (result.exitCode === 0) {
34096
+ repairedUris.push(target.uri);
34097
+ state.entries[target.uri] = { repairedAt: new Date(now).toISOString(), signature: target.signature };
34098
+ } else {
34099
+ warnings.push(indexRepairWarning(target.uri, result));
34100
+ }
34101
+ }
34102
+ if (options.dryRun !== true && repairedUris.length > 0) {
34103
+ await writeAutoRepairState(config2, state);
34104
+ }
34105
+ return { repairedUris, skippedRecentUris, warnings };
34106
+ }
34107
+ async function findStaleRecallIndexTargets(config2, options = {}) {
34108
+ const roots = await scanRoots(config2, options);
34109
+ const byUri = /* @__PURE__ */ new Map();
34110
+ for (const root of roots) {
34111
+ if (!await exists(root.path)) {
34112
+ continue;
34113
+ }
34114
+ const sidecars = await staleSidecars(root.path, root.uri);
34115
+ if (options.collapseToRoots === true) {
34116
+ if (sidecars.length === 0) {
34117
+ continue;
34118
+ }
34119
+ const parts = sidecars.map((sidecar) => `${sidecar.relativePath}
34120
+ ${sidecar.content}`);
34121
+ byUri.set(root.uri, {
34122
+ parts,
34123
+ staleCount: sidecars.length,
34124
+ uri: root.uri
34125
+ });
34126
+ continue;
34127
+ }
34128
+ for (const sidecar of sidecars) {
34129
+ const current = byUri.get(sidecar.uri) ?? { parts: [], staleCount: 0, uri: sidecar.uri };
34130
+ current.parts.push(`${sidecar.relativePath}
34131
+ ${sidecar.content}`);
34132
+ current.staleCount += 1;
34133
+ byUri.set(sidecar.uri, current);
34134
+ }
34135
+ }
34136
+ return [...byUri.values()].map((target) => ({
34137
+ signature: sha256([target.uri, ...target.parts.sort()].join("\n---\n")),
34138
+ staleCount: target.staleCount,
34139
+ uri: target.uri
34140
+ }));
34141
+ }
34142
+ function formatRecallIndexRepairMessages(result, options = {}) {
34143
+ const messages = [];
34144
+ for (const uri of result.repairedUris) {
34145
+ messages.push(
34146
+ `${options.dryRun === true ? "Would auto-reindex stale recall scope" : "Auto-reindexed stale recall scope"}: ${uri}`
34147
+ );
34148
+ }
34149
+ for (const warning2 of result.warnings) {
34150
+ messages.push(`Auto-index repair warning: ${warning2}`);
34151
+ }
34152
+ return messages;
34153
+ }
34154
+ function filterStaleRecallSummaryRows(output) {
34155
+ return output.split(/\r?\n/).filter((line) => !isStaleRecallSummaryRow(line)).join("\n").trim();
34156
+ }
34157
+ function isStaleRecallSummaryRow(line) {
34158
+ return /viking:\/\/\S+\/\.(?:abstract|overview)\.md\b/.test(line) && isStaleSummary(line);
34159
+ }
34160
+ async function scanRoots(config2, options) {
34161
+ const accountRoot = (0, import_node_path2.join)(config2.agentContextHome, "data", "viking", config2.account);
34162
+ const roots = [
34163
+ {
34164
+ path: (0, import_node_path2.join)(accountRoot, "user", uriSegment(config2.user), "memories"),
34165
+ uri: `viking://user/${uriSegment(config2.user)}/memories`
34166
+ }
34167
+ ];
34168
+ const query = options.query;
34169
+ if (query) {
34170
+ const project = await inferProjectFromQuery(config2.manifestPath, query);
34171
+ const projectPath = project?.uri.startsWith("viking://resources/") ? (0, import_node_path2.join)(accountRoot, "resources", ...project.uri.slice("viking://resources/".length).split("/")) : void 0;
34172
+ if (project && projectPath) {
34173
+ roots.push({ path: projectPath, uri: project.uri });
34174
+ }
34175
+ }
34176
+ if (options.includeManifestResources === true) {
34177
+ roots.push(...await manifestResourceRoots(config2, accountRoot));
34178
+ }
34179
+ const scanAgentSkills = options.includeAgentSkills === true || (query ? /\bskills?\b/.test(query.toLowerCase()) : false);
34180
+ if (scanAgentSkills) {
34181
+ roots.push({ path: (0, import_node_path2.join)(accountRoot, "resources", "agent-skills"), uri: "viking://resources/agent-skills" });
34182
+ }
34183
+ return dedupeRoots(roots);
34184
+ }
34185
+ async function manifestResourceRoots(config2, accountRoot) {
34186
+ try {
34187
+ const manifest = await readSeedManifest(config2.manifestPath);
34188
+ return manifest.projects.filter((project) => project.uri.startsWith("viking://resources/")).map((project) => ({
34189
+ path: (0, import_node_path2.join)(accountRoot, "resources", ...project.uri.slice("viking://resources/".length).split("/")),
34190
+ uri: project.uri
34191
+ }));
34192
+ } catch (_err) {
34193
+ return [];
34194
+ }
34195
+ }
34196
+ function dedupeRoots(roots) {
34197
+ const seen = /* @__PURE__ */ new Set();
34198
+ const deduped = [];
34199
+ for (const root of roots) {
34200
+ if (seen.has(root.uri)) {
34201
+ continue;
34202
+ }
34203
+ seen.add(root.uri);
34204
+ deduped.push(root);
34205
+ }
34206
+ return deduped;
34207
+ }
34208
+ async function staleSidecars(rootPath, rootUri) {
34209
+ const results = [];
34210
+ async function visit(path, depth) {
34211
+ let entries;
34212
+ try {
34213
+ entries = await (0, import_promises3.readdir)(path, { withFileTypes: true });
34214
+ } catch (_err) {
34215
+ return;
34216
+ }
34217
+ for (const entry of entries) {
34218
+ const childPath = (0, import_node_path2.join)(path, entry.name);
34219
+ if (entry.isFile() && isSummarySidecar(entry.name)) {
34220
+ let content;
34221
+ try {
34222
+ content = await (0, import_promises3.readFile)(childPath, "utf8");
34223
+ } catch (_err) {
34224
+ continue;
34225
+ }
34226
+ if (!isStaleSummary(content)) {
34227
+ continue;
34228
+ }
34229
+ const parentPath = (0, import_node_path2.dirname)(childPath);
34230
+ const parentRelative = toPosixPath((0, import_node_path2.relative)(rootPath, parentPath));
34231
+ const relativePath = toPosixPath((0, import_node_path2.relative)(rootPath, childPath));
34232
+ results.push({
34233
+ content: content.trim(),
34234
+ relativePath,
34235
+ uri: parentRelative ? `${trimLocalRootUri(rootUri)}/${parentRelative}` : trimLocalRootUri(rootUri)
34236
+ });
34237
+ } else if (entry.isDirectory() && depth < MAX_SCAN_DEPTH) {
34238
+ await visit(childPath, depth + 1);
34239
+ }
34240
+ }
34241
+ }
34242
+ await visit(rootPath, 0);
34243
+ return results;
34244
+ }
34245
+ function isSummarySidecar(name) {
34246
+ return name === ".abstract.md" || name === ".overview.md";
34247
+ }
34248
+ function isStaleSummary(content) {
34249
+ return content.includes("[Directory overview is not ready]") || content.includes("[Directory abstract is not ready]");
34250
+ }
34251
+ function trimLocalRootUri(uri) {
34252
+ return uri.endsWith("/") ? uri.slice(0, -1) : uri;
34253
+ }
34254
+ async function readAutoRepairState(config2) {
34255
+ const raw = await readFileIfExists(autoRepairStatePath(config2));
34256
+ if (!raw) {
34257
+ return { entries: {}, version: 1 };
34258
+ }
34259
+ try {
34260
+ const parsed = JSON.parse(raw);
34261
+ if (!isJsonObject(parsed) || parsed.version !== 1 || !isJsonObject(parsed.entries)) {
34262
+ return { entries: {}, version: 1 };
34263
+ }
34264
+ const entries = {};
34265
+ for (const [uri, entry] of Object.entries(parsed.entries)) {
34266
+ if (isJsonObject(entry) && typeof entry.signature === "string" && typeof entry.repairedAt === "string") {
34267
+ entries[uri] = { repairedAt: entry.repairedAt, signature: entry.signature };
34268
+ }
34269
+ }
34270
+ return { entries, version: 1 };
34271
+ } catch (_err) {
34272
+ return { entries: {}, version: 1 };
34273
+ }
34274
+ }
34275
+ async function writeAutoRepairState(config2, state) {
34276
+ const path = autoRepairStatePath(config2);
34277
+ await ensureDirectory((0, import_node_path2.dirname)(path), false);
34278
+ await (0, import_promises3.writeFile)(path, `${JSON.stringify(state, void 0, 2)}
34279
+ `, { encoding: "utf8", mode: 384 });
34280
+ }
34281
+ function autoRepairStatePath(config2) {
34282
+ return (0, import_node_path2.join)(config2.agentContextHome, AUTO_REPAIR_STATE_FILE);
34283
+ }
34284
+ function indexRepairWarning(uri, result) {
34285
+ return `${uri}: ${result.stderr.trim() || result.stdout.trim() || "reindex failed"}`;
34286
+ }
34287
+
34046
34288
  // src/memory_hygiene.ts
34047
- var import_node_path2 = require("node:path");
34289
+ var import_node_path3 = require("node:path");
34048
34290
  var HYGIENE_SOURCES_HEADING = "## Threadnote Hygiene Sources";
34049
34291
  var STALE_HANDOFF_AGE_MS = 14 * 24 * 60 * 60 * 1e3;
34050
34292
  function parseMemoryDocument(uri, content) {
@@ -34327,7 +34569,7 @@ function branchFromBody(body) {
34327
34569
  return branch?.split(/\s+/)[0]?.replace(/[.,;:]+$/g, "");
34328
34570
  }
34329
34571
  function topicFromUri(uri) {
34330
- const name = (0, import_node_path2.basename)(uri).replace(/\.md$/, "");
34572
+ const name = (0, import_node_path3.basename)(uri).replace(/\.md$/, "");
34331
34573
  return name.startsWith("threadnote-") ? void 0 : name;
34332
34574
  }
34333
34575
  function parseProjectFromUri(uri) {
@@ -34357,7 +34599,7 @@ function preferredKeepRecord(records, topic) {
34357
34599
  }
34358
34600
  function isStableRecord(record2, topic) {
34359
34601
  const recordTopic = topic ?? topicForRecord(record2);
34360
- return recordTopic !== void 0 && (0, import_node_path2.basename)(record2.uri) === `${uriSegment(recordTopic)}.md`;
34602
+ return recordTopic !== void 0 && (0, import_node_path3.basename)(record2.uri) === `${uriSegment(recordTopic)}.md`;
34361
34603
  }
34362
34604
  function sortedNewestFirst(records) {
34363
34605
  return [...records].sort((left, right) => {
@@ -34469,16 +34711,9 @@ function formatPlanSection(title, lines) {
34469
34711
  }
34470
34712
 
34471
34713
  // src/share.ts
34472
- var import_promises3 = require("node:fs/promises");
34714
+ var import_promises4 = require("node:fs/promises");
34473
34715
  var import_node_os2 = require("node:os");
34474
- var import_node_path3 = require("node:path");
34475
-
34476
- // src/runtime.ts
34477
- function withIdentity(config2, args) {
34478
- return [...args, "--account", config2.account, "--user", config2.user, "--agent-id", config2.agentId];
34479
- }
34480
-
34481
- // src/share.ts
34716
+ var import_node_path4 = require("node:path");
34482
34717
  var TEAMS_FILE_VERSION = 1;
34483
34718
  var SHARED_SEGMENT = "shared";
34484
34719
  var SHAREABLE_MEMORY_KIND_DIRS = ["durable"];
@@ -34585,7 +34820,7 @@ function autoShareState(config2) {
34585
34820
  return state;
34586
34821
  }
34587
34822
  function pendingReindexesPath(config2) {
34588
- return (0, import_node_path3.join)(config2.agentContextHome, "share", "auto-sync-pending-reindexes.json");
34823
+ return (0, import_node_path4.join)(config2.agentContextHome, "share", "auto-sync-pending-reindexes.json");
34589
34824
  }
34590
34825
  async function loadPendingReindexes(config2, state) {
34591
34826
  const raw = await readFileIfExists(pendingReindexesPath(config2));
@@ -34622,18 +34857,18 @@ async function loadPendingReindexes(config2, state) {
34622
34857
  async function writePendingReindexes(config2, state) {
34623
34858
  const path = pendingReindexesPath(config2);
34624
34859
  if (state.pendingReindexes.size === 0) {
34625
- await (0, import_promises3.rm)(path, { force: true });
34860
+ await (0, import_promises4.rm)(path, { force: true });
34626
34861
  return;
34627
34862
  }
34628
34863
  const contents = {
34629
34864
  teams: Object.fromEntries(state.pendingReindexes),
34630
34865
  version: 1
34631
34866
  };
34632
- await (0, import_promises3.mkdir)((0, import_node_path3.dirname)(path), { recursive: true });
34867
+ await (0, import_promises4.mkdir)((0, import_node_path4.dirname)(path), { recursive: true });
34633
34868
  const tempPath = `${path}.${process.pid}.tmp`;
34634
- await (0, import_promises3.writeFile)(tempPath, `${JSON.stringify(contents, void 0, 2)}
34869
+ await (0, import_promises4.writeFile)(tempPath, `${JSON.stringify(contents, void 0, 2)}
34635
34870
  `, { encoding: "utf8", mode: 384 });
34636
- await (0, import_promises3.rename)(tempPath, path);
34871
+ await (0, import_promises4.rename)(tempPath, path);
34637
34872
  }
34638
34873
  async function refreshShareUpdateState(config2, options) {
34639
34874
  const state = autoShareState(config2);
@@ -34729,7 +34964,7 @@ async function runShareSyncQuiet(config2, state, options) {
34729
34964
  const beforeRev = await gitOutput(worktree, ["rev-parse", "HEAD"], false);
34730
34965
  const pullResult = await runCommand(git, ["-C", worktree, "rebase", "@{u}"], { allowFailure: true });
34731
34966
  if (pullResult.exitCode !== 0) {
34732
- if (await exists((0, import_node_path3.join)(team.config.gitdir, "rebase-merge")) || await exists((0, import_node_path3.join)(team.config.gitdir, "rebase-apply"))) {
34967
+ if (await exists((0, import_node_path4.join)(team.config.gitdir, "rebase-merge")) || await exists((0, import_node_path4.join)(team.config.gitdir, "rebase-apply"))) {
34733
34968
  throw new Error(
34734
34969
  `Automatic share sync hit git conflicts in ${worktree}. Resolve them in-place, run \`git -C ${worktree} rebase --continue\` (or --abort), then rerun recall/read.`
34735
34970
  );
@@ -34814,10 +35049,10 @@ function normalizeTeamName(input) {
34814
35049
  return candidate;
34815
35050
  }
34816
35051
  function teamsFilePath(config2) {
34817
- return (0, import_node_path3.join)(config2.agentContextHome, "share", "teams.json");
35052
+ return (0, import_node_path4.join)(config2.agentContextHome, "share", "teams.json");
34818
35053
  }
34819
35054
  function teamWorktreePath(config2, team) {
34820
- return (0, import_node_path3.join)(
35055
+ return (0, import_node_path4.join)(
34821
35056
  config2.agentContextHome,
34822
35057
  "data",
34823
35058
  "viking",
@@ -34830,7 +35065,7 @@ function teamWorktreePath(config2, team) {
34830
35065
  );
34831
35066
  }
34832
35067
  function teamGitdirPath(config2, team) {
34833
- return (0, import_node_path3.join)(config2.agentContextHome, "share", "teams", `${team}.gitdir`);
35068
+ return (0, import_node_path4.join)(config2.agentContextHome, "share", "teams", `${team}.gitdir`);
34834
35069
  }
34835
35070
  async function readTeamsFile(config2) {
34836
35071
  const path = teamsFilePath(config2);
@@ -34886,7 +35121,7 @@ async function resolveTeam(config2, requested) {
34886
35121
  return { config: found, name: wantName };
34887
35122
  }
34888
35123
  function workfileToVikingUri(config2, team, filePath) {
34889
- const rel = (0, import_node_path3.relative)(team.worktree, filePath).split(import_node_path3.sep).join("/");
35124
+ const rel = (0, import_node_path4.relative)(team.worktree, filePath).split(import_node_path4.sep).join("/");
34890
35125
  return `viking://user/${uriSegment(config2.user)}/memories/${SHARED_SEGMENT}/${team.name}/${rel}`;
34891
35126
  }
34892
35127
  function isInSharedNamespace(config2, uri) {
@@ -35011,13 +35246,14 @@ async function writeMemoryFile(config2, ov, uri, content, initialMode, dryRun, o
35011
35246
  }
35012
35247
  return;
35013
35248
  }
35014
- const stagingDir = await (0, import_promises3.mkdtemp)((0, import_node_path3.join)((0, import_node_os2.tmpdir)(), "threadnote-share-"));
35015
- const tempPath = (0, import_node_path3.join)(stagingDir, "body.txt");
35249
+ const stagingDir = await (0, import_promises4.mkdtemp)((0, import_node_path4.join)((0, import_node_os2.tmpdir)(), "threadnote-share-"));
35250
+ const tempPath = (0, import_node_path4.join)(stagingDir, "body.txt");
35016
35251
  try {
35017
- await (0, import_promises3.writeFile)(tempPath, content, { encoding: "utf8", mode: 384 });
35252
+ await (0, import_promises4.writeFile)(tempPath, content, { encoding: "utf8", mode: 384 });
35018
35253
  await writeOvFileWithRetry(config2, ov, uri, tempPath, initialMode, options);
35254
+ await refreshMemoryIndex(config2, ov, uri, options);
35019
35255
  } finally {
35020
- await (0, import_promises3.rm)(stagingDir, { force: true, recursive: true });
35256
+ await (0, import_promises4.rm)(stagingDir, { force: true, recursive: true });
35021
35257
  }
35022
35258
  }
35023
35259
  var BUSY_RETRY_BACKOFF_MS = [2e3, 5e3, 1e4, 2e4, 3e4];
@@ -35072,6 +35308,27 @@ async function writeOvFileWithRetry(config2, ov, uri, fromFile, initialMode, opt
35072
35308
  }
35073
35309
  }
35074
35310
  }
35311
+ async function refreshMemoryIndex(config2, ov, uri, options = {}) {
35312
+ const result = await runCommand(
35313
+ ov,
35314
+ withIdentity(config2, ["reindex", uri, "--mode", "semantic_and_vectors", "--wait", "true"]),
35315
+ { allowFailure: true }
35316
+ );
35317
+ if (result.exitCode === 0) {
35318
+ if (options.quiet !== true && result.stdout.trim()) {
35319
+ console.log(result.stdout.trim());
35320
+ }
35321
+ if (options.quiet !== true && result.stderr.trim()) {
35322
+ console.error(result.stderr.trim());
35323
+ }
35324
+ return;
35325
+ }
35326
+ if (options.quiet !== true) {
35327
+ console.error(
35328
+ `Memory stored, but index refresh failed for ${uri}: ${result.stderr.trim() || result.stdout.trim()}`
35329
+ );
35330
+ }
35331
+ }
35075
35332
  async function waitForOvQueue(ov, config2, options = {}) {
35076
35333
  const result = await runCommand(ov, withIdentity(config2, ["wait", "--timeout", "120"]), { allowFailure: true });
35077
35334
  if (options.quiet !== true && result.stdout.trim()) {
@@ -35092,7 +35349,7 @@ ${stdout2}`.toLowerCase();
35092
35349
  return output.includes("resource is busy") || output.includes("resource is being processed");
35093
35350
  }
35094
35351
  async function ingestSingleFile(ov, config2, uri, filePath, initialMode, options = {}) {
35095
- const content = await (0, import_promises3.readFile)(filePath, "utf8");
35352
+ const content = await (0, import_promises4.readFile)(filePath, "utf8");
35096
35353
  await writeMemoryFile(config2, ov, uri, content, initialMode, false, options);
35097
35354
  }
35098
35355
  async function removeMemoryUri(config2, ov, uri, dryRun, options = {}) {
@@ -35157,8 +35414,8 @@ async function listChangedFiles(worktree, beforeRev, afterRev) {
35157
35414
  const oldRel = entries[index + 1];
35158
35415
  const newRel = entries[index + 2];
35159
35416
  if (oldRel && newRel) {
35160
- changes.push({ path: (0, import_node_path3.join)(worktree, oldRel), relativePath: oldRel, status: "removed" });
35161
- changes.push({ path: (0, import_node_path3.join)(worktree, newRel), relativePath: newRel, status: "added" });
35417
+ changes.push({ path: (0, import_node_path4.join)(worktree, oldRel), relativePath: oldRel, status: "removed" });
35418
+ changes.push({ path: (0, import_node_path4.join)(worktree, newRel), relativePath: newRel, status: "added" });
35162
35419
  }
35163
35420
  index += 3;
35164
35421
  continue;
@@ -35166,7 +35423,7 @@ async function listChangedFiles(worktree, beforeRev, afterRev) {
35166
35423
  const rel = entries[index + 1];
35167
35424
  if (rel) {
35168
35425
  const status = head === "A" ? "added" : head === "D" ? "removed" : "modified";
35169
- changes.push({ path: (0, import_node_path3.join)(worktree, rel), relativePath: rel, status });
35426
+ changes.push({ path: (0, import_node_path4.join)(worktree, rel), relativePath: rel, status });
35170
35427
  }
35171
35428
  index += 2;
35172
35429
  }
@@ -35516,6 +35773,14 @@ async function runRecallTool(config2, params) {
35516
35773
  cwd: params.callerCwd,
35517
35774
  includeProcessCwd: false
35518
35775
  });
35776
+ let indexRepairMessages;
35777
+ try {
35778
+ const ov = await requiredOpenVikingCli2();
35779
+ const indexRepair = await repairStaleRecallIndex(config2, ov, { query: projectQuery });
35780
+ indexRepairMessages = formatRecallIndexRepairMessages(indexRepair);
35781
+ } catch (err) {
35782
+ indexRepairMessages = [`Auto-index repair warning: ${errorMessage(err)}`];
35783
+ }
35519
35784
  const contextualParams = { ...params, query };
35520
35785
  const baseArgs = [
35521
35786
  "search",
@@ -35532,8 +35797,13 @@ async function runRecallTool(config2, params) {
35532
35797
  return semanticResult;
35533
35798
  }
35534
35799
  const sections = [];
35535
- if (firstContent.text) {
35536
- sections.push(firstContent.text);
35800
+ const semanticText = filterStaleRecallSummaryRows(firstContent.text);
35801
+ const filteredSemanticText = semanticText !== firstContent.text.trim();
35802
+ if (semanticText) {
35803
+ sections.push(semanticText);
35804
+ }
35805
+ if (indexRepairMessages.length > 0) {
35806
+ sections.push(indexRepairMessages.join("\n"));
35537
35807
  }
35538
35808
  const seededSection = await seededResourcesSection(config2, contextualParams, projectQuery);
35539
35809
  if (seededSection) {
@@ -35554,7 +35824,7 @@ ${exactMatches}`);
35554
35824
  for (const warning2 of syncWarnings) {
35555
35825
  sections.push(`Auto-sync warning: ${warning2}`);
35556
35826
  }
35557
- if (sections.length <= 1) {
35827
+ if (sections.length <= 1 && !filteredSemanticText) {
35558
35828
  return semanticResult;
35559
35829
  }
35560
35830
  return { ...semanticResult, content: [{ type: "text", text: sections.join("\n\n") }] };
@@ -35592,7 +35862,7 @@ async function seededResourcesSection(config2, params, projectQuery) {
35592
35862
  if (result.exitCode !== 0) {
35593
35863
  return void 0;
35594
35864
  }
35595
- const body = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n");
35865
+ const body = filterStaleRecallSummaryRows([result.stdout.trim(), result.stderr.trim()].filter(Boolean).join("\n"));
35596
35866
  if (!body) {
35597
35867
  return void 0;
35598
35868
  }
@@ -35914,7 +36184,7 @@ async function scopedCompactRecords(config2, options) {
35914
36184
  const uriDirectory = memoryUriDirectoryForCompact(config2, kind, options.project);
35915
36185
  let entries;
35916
36186
  try {
35917
- entries = await (0, import_promises4.readdir)(directory, { withFileTypes: true });
36187
+ entries = await (0, import_promises5.readdir)(directory, { withFileTypes: true });
35918
36188
  } catch (_err) {
35919
36189
  continue;
35920
36190
  }
@@ -35922,7 +36192,7 @@ async function scopedCompactRecords(config2, options) {
35922
36192
  if (!entry.isFile() || entry.name.startsWith(".") || !entry.name.endsWith(".md")) {
35923
36193
  continue;
35924
36194
  }
35925
- const content = await readTextIfExists((0, import_node_path4.join)(directory, entry.name));
36195
+ const content = await readTextIfExists((0, import_node_path5.join)(directory, entry.name));
35926
36196
  if (!content) {
35927
36197
  continue;
35928
36198
  }
@@ -35957,11 +36227,11 @@ function localMemoryDirectoryForCompact(config2, kind, project) {
35957
36227
  const projectSegment = uriSegment2(project);
35958
36228
  switch (kind) {
35959
36229
  case "durable":
35960
- return (0, import_node_path4.join)(root, "durable", "projects", projectSegment);
36230
+ return (0, import_node_path5.join)(root, "durable", "projects", projectSegment);
35961
36231
  case "handoff":
35962
- return (0, import_node_path4.join)(root, "handoffs", "active", projectSegment);
36232
+ return (0, import_node_path5.join)(root, "handoffs", "active", projectSegment);
35963
36233
  case "incident":
35964
- return (0, import_node_path4.join)(root, "incidents", "active", projectSegment);
36234
+ return (0, import_node_path5.join)(root, "incidents", "active", projectSegment);
35965
36235
  }
35966
36236
  }
35967
36237
  function memoryUriDirectoryForCompact(config2, kind, project) {
@@ -35981,18 +36251,18 @@ function localMemoryPathForUri(config2, uri) {
35981
36251
  if (!uri.startsWith(prefix) || uri.includes("/shared/")) {
35982
36252
  return void 0;
35983
36253
  }
35984
- const relative2 = uri.slice(prefix.length);
35985
- if (relative2.includes("..") || relative2.startsWith("/")) {
36254
+ const relative3 = uri.slice(prefix.length);
36255
+ if (relative3.includes("..") || relative3.startsWith("/")) {
35986
36256
  return void 0;
35987
36257
  }
35988
- return (0, import_node_path4.join)(localUserMemoriesRoot(config2), ...relative2.split("/"));
36258
+ return (0, import_node_path5.join)(localUserMemoriesRoot(config2), ...relative3.split("/"));
35989
36259
  }
35990
36260
  function localUserMemoriesRoot(config2) {
35991
- return (0, import_node_path4.join)(config2.agentContextHome, "data", "viking", config2.account, "user", uriSegment2(config2.user), "memories");
36261
+ return (0, import_node_path5.join)(config2.agentContextHome, "data", "viking", config2.account, "user", uriSegment2(config2.user), "memories");
35992
36262
  }
35993
36263
  async function readTextIfExists(path) {
35994
36264
  try {
35995
- return await (0, import_promises4.readFile)(path, "utf8");
36265
+ return await (0, import_promises5.readFile)(path, "utf8");
35996
36266
  } catch (_err) {
35997
36267
  return void 0;
35998
36268
  }
@@ -36327,7 +36597,7 @@ function withIdentity2(config2, args) {
36327
36597
  return [...args, "--account", config2.account, "--user", config2.user, "--agent-id", config2.agentId];
36328
36598
  }
36329
36599
  async function requiredOpenVikingCli2() {
36330
- const command2 = process.env.THREADNOTE_OV ?? await findExecutable(["ov", "openviking"]) ?? await firstExistingPath([(0, import_node_path4.join)((0, import_node_os3.homedir)(), ".local", "bin", "ov"), (0, import_node_path4.join)((0, import_node_os3.homedir)(), ".local", "bin", "openviking")]);
36600
+ const command2 = process.env.THREADNOTE_OV ?? await findExecutable(["ov", "openviking"]) ?? await firstExistingPath([(0, import_node_path5.join)((0, import_node_os3.homedir)(), ".local", "bin", "ov"), (0, import_node_path5.join)((0, import_node_os3.homedir)(), ".local", "bin", "openviking")]);
36331
36601
  if (!command2) {
36332
36602
  throw new Error("Neither ov nor openviking was found. Run threadnote install first.");
36333
36603
  }
@@ -36336,8 +36606,8 @@ async function requiredOpenVikingCli2() {
36336
36606
  async function firstExistingPath(paths) {
36337
36607
  for (const path of paths) {
36338
36608
  try {
36339
- await (0, import_promises4.access)(path);
36340
- return await (0, import_promises4.realpath)(path);
36609
+ await (0, import_promises5.access)(path);
36610
+ return await (0, import_promises5.realpath)(path);
36341
36611
  } catch (_err) {
36342
36612
  continue;
36343
36613
  }