@getrift/rift 0.1.0-beta.2 → 0.1.0-beta.21

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.
Files changed (390) hide show
  1. package/README.md +35 -9
  2. package/dist/src/auth/keychain.d.ts +9 -0
  3. package/dist/src/auth/keychain.d.ts.map +1 -1
  4. package/dist/src/auth/keychain.js +37 -0
  5. package/dist/src/auth/keychain.js.map +1 -1
  6. package/dist/src/capture/auto-capture.d.ts +7 -0
  7. package/dist/src/capture/auto-capture.d.ts.map +1 -1
  8. package/dist/src/capture/auto-capture.js +82 -15
  9. package/dist/src/capture/auto-capture.js.map +1 -1
  10. package/dist/src/capture/auto-repair.d.ts +110 -0
  11. package/dist/src/capture/auto-repair.d.ts.map +1 -0
  12. package/dist/src/capture/auto-repair.js +269 -0
  13. package/dist/src/capture/auto-repair.js.map +1 -0
  14. package/dist/src/capture/codex-cli-triage-provider.d.ts.map +1 -1
  15. package/dist/src/capture/codex-cli-triage-provider.js +4 -3
  16. package/dist/src/capture/codex-cli-triage-provider.js.map +1 -1
  17. package/dist/src/capture/observability.d.ts +42 -0
  18. package/dist/src/capture/observability.d.ts.map +1 -1
  19. package/dist/src/capture/observability.js +45 -4
  20. package/dist/src/capture/observability.js.map +1 -1
  21. package/dist/src/capture/recover-quarantine.d.ts +260 -0
  22. package/dist/src/capture/recover-quarantine.d.ts.map +1 -0
  23. package/dist/src/capture/recover-quarantine.js +522 -0
  24. package/dist/src/capture/recover-quarantine.js.map +1 -0
  25. package/dist/src/cli/commands/backfill.d.ts.map +1 -1
  26. package/dist/src/cli/commands/backfill.js +5 -2
  27. package/dist/src/cli/commands/backfill.js.map +1 -1
  28. package/dist/src/cli/commands/capture-recover.d.ts +40 -0
  29. package/dist/src/cli/commands/capture-recover.d.ts.map +1 -0
  30. package/dist/src/cli/commands/capture-recover.js +184 -0
  31. package/dist/src/cli/commands/capture-recover.js.map +1 -0
  32. package/dist/src/cli/commands/capture.d.ts.map +1 -1
  33. package/dist/src/cli/commands/capture.js +96 -5
  34. package/dist/src/cli/commands/capture.js.map +1 -1
  35. package/dist/src/cli/commands/doctor.d.ts +6 -0
  36. package/dist/src/cli/commands/doctor.d.ts.map +1 -0
  37. package/dist/src/cli/commands/doctor.js +242 -0
  38. package/dist/src/cli/commands/doctor.js.map +1 -0
  39. package/dist/src/cli/commands/feedback.d.ts +12 -0
  40. package/dist/src/cli/commands/feedback.d.ts.map +1 -1
  41. package/dist/src/cli/commands/feedback.js +93 -4
  42. package/dist/src/cli/commands/feedback.js.map +1 -1
  43. package/dist/src/cli/commands/mcp-install.js +5 -2
  44. package/dist/src/cli/commands/mcp-install.js.map +1 -1
  45. package/dist/src/cli/commands/menubar.d.ts +80 -0
  46. package/dist/src/cli/commands/menubar.d.ts.map +1 -0
  47. package/dist/src/cli/commands/menubar.js +388 -0
  48. package/dist/src/cli/commands/menubar.js.map +1 -0
  49. package/dist/src/cli/commands/onboard.d.ts +143 -5
  50. package/dist/src/cli/commands/onboard.d.ts.map +1 -1
  51. package/dist/src/cli/commands/onboard.js +844 -188
  52. package/dist/src/cli/commands/onboard.js.map +1 -1
  53. package/dist/src/cli/commands/rebuild.d.ts.map +1 -1
  54. package/dist/src/cli/commands/rebuild.js +6 -3
  55. package/dist/src/cli/commands/rebuild.js.map +1 -1
  56. package/dist/src/cli/commands/reconcile.d.ts.map +1 -1
  57. package/dist/src/cli/commands/reconcile.js +12 -0
  58. package/dist/src/cli/commands/reconcile.js.map +1 -1
  59. package/dist/src/cli/commands/review.d.ts.map +1 -1
  60. package/dist/src/cli/commands/review.js +22 -7
  61. package/dist/src/cli/commands/review.js.map +1 -1
  62. package/dist/src/cli/commands/search.d.ts +2 -0
  63. package/dist/src/cli/commands/search.d.ts.map +1 -1
  64. package/dist/src/cli/commands/search.js +34 -4
  65. package/dist/src/cli/commands/search.js.map +1 -1
  66. package/dist/src/cli/commands/status.d.ts +9 -7
  67. package/dist/src/cli/commands/status.d.ts.map +1 -1
  68. package/dist/src/cli/commands/status.js +117 -12
  69. package/dist/src/cli/commands/status.js.map +1 -1
  70. package/dist/src/cli/commands/token-issue.d.ts.map +1 -1
  71. package/dist/src/cli/commands/token-issue.js +9 -1
  72. package/dist/src/cli/commands/token-issue.js.map +1 -1
  73. package/dist/src/cli/commands/triage.d.ts.map +1 -1
  74. package/dist/src/cli/commands/triage.js +7 -5
  75. package/dist/src/cli/commands/triage.js.map +1 -1
  76. package/dist/src/cli/commands/update.d.ts +80 -0
  77. package/dist/src/cli/commands/update.d.ts.map +1 -0
  78. package/dist/src/cli/commands/update.js +390 -0
  79. package/dist/src/cli/commands/update.js.map +1 -0
  80. package/dist/src/cli/default-config-path.d.ts +15 -0
  81. package/dist/src/cli/default-config-path.d.ts.map +1 -0
  82. package/dist/src/cli/default-config-path.js +27 -0
  83. package/dist/src/cli/default-config-path.js.map +1 -0
  84. package/dist/src/cli/feedback/feedback-config.d.ts +46 -0
  85. package/dist/src/cli/feedback/feedback-config.d.ts.map +1 -1
  86. package/dist/src/cli/feedback/feedback-config.js +130 -4
  87. package/dist/src/cli/feedback/feedback-config.js.map +1 -1
  88. package/dist/src/cli/feedback/feedback-history.d.ts +7 -0
  89. package/dist/src/cli/feedback/feedback-history.d.ts.map +1 -1
  90. package/dist/src/cli/feedback/feedback-history.js +39 -9
  91. package/dist/src/cli/feedback/feedback-history.js.map +1 -1
  92. package/dist/src/cli/feedback/feedback-payload.d.ts +22 -1
  93. package/dist/src/cli/feedback/feedback-payload.d.ts.map +1 -1
  94. package/dist/src/cli/feedback/feedback-payload.js.map +1 -1
  95. package/dist/src/cli/feedback/feedback-relay.d.ts +2 -2
  96. package/dist/src/cli/feedback/feedback-relay.d.ts.map +1 -1
  97. package/dist/src/cli/feedback/feedback-relay.js.map +1 -1
  98. package/dist/src/cli/feedback/invite.d.ts +17 -0
  99. package/dist/src/cli/feedback/invite.d.ts.map +1 -0
  100. package/dist/src/cli/feedback/invite.js +67 -0
  101. package/dist/src/cli/feedback/invite.js.map +1 -0
  102. package/dist/src/cli/feedback/relay-secret-store.d.ts +32 -0
  103. package/dist/src/cli/feedback/relay-secret-store.d.ts.map +1 -0
  104. package/dist/src/cli/feedback/relay-secret-store.js +137 -0
  105. package/dist/src/cli/feedback/relay-secret-store.js.map +1 -0
  106. package/dist/src/cli/http-client.d.ts +93 -1
  107. package/dist/src/cli/http-client.d.ts.map +1 -1
  108. package/dist/src/cli/http-client.js +254 -6
  109. package/dist/src/cli/http-client.js.map +1 -1
  110. package/dist/src/cli/index.d.ts.map +1 -1
  111. package/dist/src/cli/index.js +29 -6
  112. package/dist/src/cli/index.js.map +1 -1
  113. package/dist/src/cli/postinstall-menubar.d.ts +22 -0
  114. package/dist/src/cli/postinstall-menubar.d.ts.map +1 -0
  115. package/dist/src/cli/postinstall-menubar.js +53 -0
  116. package/dist/src/cli/postinstall-menubar.js.map +1 -0
  117. package/dist/src/cli/status/friend-header.d.ts +16 -1
  118. package/dist/src/cli/status/friend-header.d.ts.map +1 -1
  119. package/dist/src/cli/status/friend-header.js +354 -26
  120. package/dist/src/cli/status/friend-header.js.map +1 -1
  121. package/dist/src/cli/status/local-signals.d.ts +18 -0
  122. package/dist/src/cli/status/local-signals.d.ts.map +1 -1
  123. package/dist/src/cli/status/local-signals.js +29 -0
  124. package/dist/src/cli/status/local-signals.js.map +1 -1
  125. package/dist/src/cli/ui.d.ts +47 -0
  126. package/dist/src/cli/ui.d.ts.map +1 -0
  127. package/dist/src/cli/ui.js +166 -0
  128. package/dist/src/cli/ui.js.map +1 -0
  129. package/dist/src/config/schema.d.ts +79 -0
  130. package/dist/src/config/schema.d.ts.map +1 -1
  131. package/dist/src/config/schema.js +44 -0
  132. package/dist/src/config/schema.js.map +1 -1
  133. package/dist/src/diagnostics/codex-preflight.d.ts +33 -0
  134. package/dist/src/diagnostics/codex-preflight.d.ts.map +1 -0
  135. package/dist/src/diagnostics/codex-preflight.js +75 -0
  136. package/dist/src/diagnostics/codex-preflight.js.map +1 -0
  137. package/dist/src/diagnostics/doctor.d.ts +114 -0
  138. package/dist/src/diagnostics/doctor.d.ts.map +1 -0
  139. package/dist/src/diagnostics/doctor.js +352 -0
  140. package/dist/src/diagnostics/doctor.js.map +1 -0
  141. package/dist/src/diagnostics/notify.d.ts +90 -0
  142. package/dist/src/diagnostics/notify.d.ts.map +1 -0
  143. package/dist/src/diagnostics/notify.js +177 -0
  144. package/dist/src/diagnostics/notify.js.map +1 -0
  145. package/dist/src/diagnostics/repair-prompt.d.ts +49 -0
  146. package/dist/src/diagnostics/repair-prompt.d.ts.map +1 -0
  147. package/dist/src/diagnostics/repair-prompt.js +223 -0
  148. package/dist/src/diagnostics/repair-prompt.js.map +1 -0
  149. package/dist/src/ingestion/inbox-core/conversation-fingerprint.d.ts +2 -0
  150. package/dist/src/ingestion/inbox-core/conversation-fingerprint.d.ts.map +1 -0
  151. package/dist/src/ingestion/inbox-core/conversation-fingerprint.js +27 -0
  152. package/dist/src/ingestion/inbox-core/conversation-fingerprint.js.map +1 -0
  153. package/dist/src/ingestion/inbox-core/conversation-key.d.ts +2 -0
  154. package/dist/src/ingestion/inbox-core/conversation-key.d.ts.map +1 -0
  155. package/dist/src/ingestion/inbox-core/conversation-key.js +31 -0
  156. package/dist/src/ingestion/inbox-core/conversation-key.js.map +1 -0
  157. package/dist/src/ingestion/inbox-core/extensions.d.ts +3 -0
  158. package/dist/src/ingestion/inbox-core/extensions.d.ts.map +1 -0
  159. package/dist/src/ingestion/inbox-core/extensions.js +16 -0
  160. package/dist/src/ingestion/inbox-core/extensions.js.map +1 -0
  161. package/dist/src/ingestion/inbox-core/idempotency.d.ts +2 -0
  162. package/dist/src/ingestion/inbox-core/idempotency.d.ts.map +1 -0
  163. package/dist/src/ingestion/inbox-core/idempotency.js +22 -0
  164. package/dist/src/ingestion/inbox-core/idempotency.js.map +1 -0
  165. package/dist/src/ingestion/inbox-core/index.d.ts +20 -0
  166. package/dist/src/ingestion/inbox-core/index.d.ts.map +1 -0
  167. package/dist/src/ingestion/inbox-core/index.js +20 -0
  168. package/dist/src/ingestion/inbox-core/index.js.map +1 -0
  169. package/dist/src/ingestion/inbox-core/source-detection.d.ts +2 -0
  170. package/dist/src/ingestion/inbox-core/source-detection.d.ts.map +1 -0
  171. package/dist/src/ingestion/inbox-core/source-detection.js +23 -0
  172. package/dist/src/ingestion/inbox-core/source-detection.js.map +1 -0
  173. package/dist/src/ingestion/inbox-core/source-sniffer.d.ts +11 -0
  174. package/dist/src/ingestion/inbox-core/source-sniffer.d.ts.map +1 -0
  175. package/dist/src/ingestion/inbox-core/source-sniffer.js +69 -0
  176. package/dist/src/ingestion/inbox-core/source-sniffer.js.map +1 -0
  177. package/dist/src/ingestion/inbox-core/zip-sniffer.d.ts +70 -0
  178. package/dist/src/ingestion/inbox-core/zip-sniffer.d.ts.map +1 -0
  179. package/dist/src/ingestion/inbox-core/zip-sniffer.js +161 -0
  180. package/dist/src/ingestion/inbox-core/zip-sniffer.js.map +1 -0
  181. package/dist/src/ingestion/inbox-watcher.d.ts.map +1 -1
  182. package/dist/src/ingestion/inbox-watcher.js +34 -50
  183. package/dist/src/ingestion/inbox-watcher.js.map +1 -1
  184. package/dist/src/ingestion/indexer.d.ts +7 -0
  185. package/dist/src/ingestion/indexer.d.ts.map +1 -1
  186. package/dist/src/ingestion/indexer.js +36 -2
  187. package/dist/src/ingestion/indexer.js.map +1 -1
  188. package/dist/src/ingestion/metadata-extraction.d.ts +8 -5
  189. package/dist/src/ingestion/metadata-extraction.d.ts.map +1 -1
  190. package/dist/src/ingestion/metadata-extraction.js +24 -5
  191. package/dist/src/ingestion/metadata-extraction.js.map +1 -1
  192. package/dist/src/ingestion/skip-quarantine.d.ts +10 -0
  193. package/dist/src/ingestion/skip-quarantine.d.ts.map +1 -0
  194. package/dist/src/ingestion/skip-quarantine.js +35 -0
  195. package/dist/src/ingestion/skip-quarantine.js.map +1 -0
  196. package/dist/src/jobs/handlers/compact.d.ts.map +1 -1
  197. package/dist/src/jobs/handlers/compact.js +30 -4
  198. package/dist/src/jobs/handlers/compact.js.map +1 -1
  199. package/dist/src/jobs/handlers/dedupe-conversations.d.ts +134 -0
  200. package/dist/src/jobs/handlers/dedupe-conversations.d.ts.map +1 -0
  201. package/dist/src/jobs/handlers/dedupe-conversations.js +371 -0
  202. package/dist/src/jobs/handlers/dedupe-conversations.js.map +1 -0
  203. package/dist/src/jobs/handlers/ingest.d.ts.map +1 -1
  204. package/dist/src/jobs/handlers/ingest.js +295 -41
  205. package/dist/src/jobs/handlers/ingest.js.map +1 -1
  206. package/dist/src/jobs/handlers/reconcile.d.ts +28 -0
  207. package/dist/src/jobs/handlers/reconcile.d.ts.map +1 -1
  208. package/dist/src/jobs/handlers/reconcile.js +145 -19
  209. package/dist/src/jobs/handlers/reconcile.js.map +1 -1
  210. package/dist/src/jobs/handlers/reindex.d.ts.map +1 -1
  211. package/dist/src/jobs/handlers/reindex.js +13 -2
  212. package/dist/src/jobs/handlers/reindex.js.map +1 -1
  213. package/dist/src/jobs/handlers/save.d.ts.map +1 -1
  214. package/dist/src/jobs/handlers/save.js +57 -3
  215. package/dist/src/jobs/handlers/save.js.map +1 -1
  216. package/dist/src/jobs/queue.d.ts +51 -1
  217. package/dist/src/jobs/queue.d.ts.map +1 -1
  218. package/dist/src/jobs/queue.js +466 -26
  219. package/dist/src/jobs/queue.js.map +1 -1
  220. package/dist/src/jobs/worker-entry.d.ts.map +1 -1
  221. package/dist/src/jobs/worker-entry.js +35 -7
  222. package/dist/src/jobs/worker-entry.js.map +1 -1
  223. package/dist/src/jobs/worker-process.d.ts +11 -0
  224. package/dist/src/jobs/worker-process.d.ts.map +1 -1
  225. package/dist/src/jobs/worker-process.js +37 -4
  226. package/dist/src/jobs/worker-process.js.map +1 -1
  227. package/dist/src/main.js +199 -46
  228. package/dist/src/main.js.map +1 -1
  229. package/dist/src/mcp/errors.d.ts.map +1 -1
  230. package/dist/src/mcp/errors.js +20 -1
  231. package/dist/src/mcp/errors.js.map +1 -1
  232. package/dist/src/mcp/server.d.ts.map +1 -1
  233. package/dist/src/mcp/server.js +43 -3
  234. package/dist/src/mcp/server.js.map +1 -1
  235. package/dist/src/mcp/tools/context-pack.d.ts.map +1 -1
  236. package/dist/src/mcp/tools/context-pack.js +164 -23
  237. package/dist/src/mcp/tools/context-pack.js.map +1 -1
  238. package/dist/src/mcp/tools/search.d.ts +6 -2
  239. package/dist/src/mcp/tools/search.d.ts.map +1 -1
  240. package/dist/src/mcp/tools/search.js +35 -4
  241. package/dist/src/mcp/tools/search.js.map +1 -1
  242. package/dist/src/observability/embedding-events.d.ts +52 -0
  243. package/dist/src/observability/embedding-events.d.ts.map +1 -0
  244. package/dist/src/observability/embedding-events.js +149 -0
  245. package/dist/src/observability/embedding-events.js.map +1 -0
  246. package/dist/src/observability/index-events.d.ts +70 -0
  247. package/dist/src/observability/index-events.d.ts.map +1 -0
  248. package/dist/src/observability/index-events.js +148 -0
  249. package/dist/src/observability/index-events.js.map +1 -0
  250. package/dist/src/observability/onboarding-metric.d.ts +131 -0
  251. package/dist/src/observability/onboarding-metric.d.ts.map +1 -0
  252. package/dist/src/observability/onboarding-metric.js +351 -0
  253. package/dist/src/observability/onboarding-metric.js.map +1 -0
  254. package/dist/src/observability/tool-usage-stats.d.ts +77 -4
  255. package/dist/src/observability/tool-usage-stats.d.ts.map +1 -1
  256. package/dist/src/observability/tool-usage-stats.js +112 -32
  257. package/dist/src/observability/tool-usage-stats.js.map +1 -1
  258. package/dist/src/observability/tool-usage.d.ts +100 -7
  259. package/dist/src/observability/tool-usage.d.ts.map +1 -1
  260. package/dist/src/observability/tool-usage.js +196 -33
  261. package/dist/src/observability/tool-usage.js.map +1 -1
  262. package/dist/src/observability/version-check.d.ts +71 -0
  263. package/dist/src/observability/version-check.d.ts.map +1 -0
  264. package/dist/src/observability/version-check.js +198 -0
  265. package/dist/src/observability/version-check.js.map +1 -0
  266. package/dist/src/providers/basic-metadata-extraction.d.ts +60 -0
  267. package/dist/src/providers/basic-metadata-extraction.d.ts.map +1 -0
  268. package/dist/src/providers/basic-metadata-extraction.js +114 -0
  269. package/dist/src/providers/basic-metadata-extraction.js.map +1 -0
  270. package/dist/src/providers/codex-cli-metadata-extraction.d.ts +1 -0
  271. package/dist/src/providers/codex-cli-metadata-extraction.d.ts.map +1 -1
  272. package/dist/src/providers/codex-cli-metadata-extraction.js +6 -2
  273. package/dist/src/providers/codex-cli-metadata-extraction.js.map +1 -1
  274. package/dist/src/providers/codex-cli-model.d.ts +61 -0
  275. package/dist/src/providers/codex-cli-model.d.ts.map +1 -0
  276. package/dist/src/providers/codex-cli-model.js +194 -0
  277. package/dist/src/providers/codex-cli-model.js.map +1 -0
  278. package/dist/src/providers/codex-cli-runner.d.ts +39 -0
  279. package/dist/src/providers/codex-cli-runner.d.ts.map +1 -1
  280. package/dist/src/providers/codex-cli-runner.js +234 -48
  281. package/dist/src/providers/codex-cli-runner.js.map +1 -1
  282. package/dist/src/providers/conversation-generation.d.ts.map +1 -1
  283. package/dist/src/providers/conversation-generation.js +43 -6
  284. package/dist/src/providers/conversation-generation.js.map +1 -1
  285. package/dist/src/providers/ollama-embed.d.ts +2 -1
  286. package/dist/src/providers/ollama-embed.d.ts.map +1 -1
  287. package/dist/src/providers/ollama-embed.js +1 -0
  288. package/dist/src/providers/ollama-embed.js.map +1 -1
  289. package/dist/src/providers/openai-metadata-extraction.d.ts +3 -3
  290. package/dist/src/providers/openai-metadata-extraction.d.ts.map +1 -1
  291. package/dist/src/providers/openai-metadata-extraction.js +18 -3
  292. package/dist/src/providers/openai-metadata-extraction.js.map +1 -1
  293. package/dist/src/providers/placeholder-embed.d.ts +56 -0
  294. package/dist/src/providers/placeholder-embed.d.ts.map +1 -0
  295. package/dist/src/providers/placeholder-embed.js +64 -0
  296. package/dist/src/providers/placeholder-embed.js.map +1 -0
  297. package/dist/src/providers/stub.d.ts +2 -0
  298. package/dist/src/providers/stub.d.ts.map +1 -1
  299. package/dist/src/providers/stub.js +2 -0
  300. package/dist/src/providers/stub.js.map +1 -1
  301. package/dist/src/providers/types.d.ts +11 -0
  302. package/dist/src/providers/types.d.ts.map +1 -1
  303. package/dist/src/providers/voyage.d.ts +2 -1
  304. package/dist/src/providers/voyage.d.ts.map +1 -1
  305. package/dist/src/providers/voyage.js +1 -0
  306. package/dist/src/providers/voyage.js.map +1 -1
  307. package/dist/src/retrieval/compact.d.ts +116 -2
  308. package/dist/src/retrieval/compact.d.ts.map +1 -1
  309. package/dist/src/retrieval/compact.js +158 -5
  310. package/dist/src/retrieval/compact.js.map +1 -1
  311. package/dist/src/retrieval/context-pack.d.ts +114 -0
  312. package/dist/src/retrieval/context-pack.d.ts.map +1 -1
  313. package/dist/src/retrieval/context-pack.js +292 -8
  314. package/dist/src/retrieval/context-pack.js.map +1 -1
  315. package/dist/src/retrieval/current-truth.d.ts +360 -0
  316. package/dist/src/retrieval/current-truth.d.ts.map +1 -0
  317. package/dist/src/retrieval/current-truth.js +766 -0
  318. package/dist/src/retrieval/current-truth.js.map +1 -0
  319. package/dist/src/retrieval/git-state.d.ts +53 -0
  320. package/dist/src/retrieval/git-state.d.ts.map +1 -0
  321. package/dist/src/retrieval/git-state.js +174 -0
  322. package/dist/src/retrieval/git-state.js.map +1 -0
  323. package/dist/src/retrieval/lexical.d.ts.map +1 -1
  324. package/dist/src/retrieval/lexical.js +19 -3
  325. package/dist/src/retrieval/lexical.js.map +1 -1
  326. package/dist/src/retrieval/locator-boost.d.ts +37 -0
  327. package/dist/src/retrieval/locator-boost.d.ts.map +1 -0
  328. package/dist/src/retrieval/locator-boost.js +129 -0
  329. package/dist/src/retrieval/locator-boost.js.map +1 -0
  330. package/dist/src/retrieval/report-demotion.d.ts +46 -0
  331. package/dist/src/retrieval/report-demotion.d.ts.map +1 -0
  332. package/dist/src/retrieval/report-demotion.js +169 -0
  333. package/dist/src/retrieval/report-demotion.js.map +1 -0
  334. package/dist/src/retrieval/vector.d.ts.map +1 -1
  335. package/dist/src/retrieval/vector.js +11 -2
  336. package/dist/src/retrieval/vector.js.map +1 -1
  337. package/dist/src/server/app.d.ts.map +1 -1
  338. package/dist/src/server/app.js +92 -11
  339. package/dist/src/server/app.js.map +1 -1
  340. package/dist/src/server/routes/compact.d.ts.map +1 -1
  341. package/dist/src/server/routes/compact.js +4 -1
  342. package/dist/src/server/routes/compact.js.map +1 -1
  343. package/dist/src/server/routes/context.d.ts +1 -1
  344. package/dist/src/server/routes/context.d.ts.map +1 -1
  345. package/dist/src/server/routes/context.js +2 -1
  346. package/dist/src/server/routes/context.js.map +1 -1
  347. package/dist/src/server/routes/conversations-search.d.ts.map +1 -1
  348. package/dist/src/server/routes/conversations-search.js +28 -3
  349. package/dist/src/server/routes/conversations-search.js.map +1 -1
  350. package/dist/src/server/routes/enqueue.d.ts +11 -0
  351. package/dist/src/server/routes/enqueue.d.ts.map +1 -0
  352. package/dist/src/server/routes/enqueue.js +17 -0
  353. package/dist/src/server/routes/enqueue.js.map +1 -0
  354. package/dist/src/server/routes/friend-status.d.ts +339 -3
  355. package/dist/src/server/routes/friend-status.d.ts.map +1 -1
  356. package/dist/src/server/routes/friend-status.js +447 -13
  357. package/dist/src/server/routes/friend-status.js.map +1 -1
  358. package/dist/src/server/routes/ingest.d.ts.map +1 -1
  359. package/dist/src/server/routes/ingest.js +5 -2
  360. package/dist/src/server/routes/ingest.js.map +1 -1
  361. package/dist/src/server/routes/mcp-usage.d.ts +5 -4
  362. package/dist/src/server/routes/mcp-usage.d.ts.map +1 -1
  363. package/dist/src/server/routes/mcp-usage.js.map +1 -1
  364. package/dist/src/server/routes/reconcile.d.ts.map +1 -1
  365. package/dist/src/server/routes/reconcile.js +20 -1
  366. package/dist/src/server/routes/reconcile.js.map +1 -1
  367. package/dist/src/server/routes/reindex.d.ts.map +1 -1
  368. package/dist/src/server/routes/reindex.js +4 -1
  369. package/dist/src/server/routes/reindex.js.map +1 -1
  370. package/dist/src/server/routes/save.d.ts.map +1 -1
  371. package/dist/src/server/routes/save.js +4 -1
  372. package/dist/src/server/routes/save.js.map +1 -1
  373. package/dist/src/server/routes/search.d.ts +1 -1
  374. package/dist/src/server/routes/search.d.ts.map +1 -1
  375. package/dist/src/server/routes/search.js +253 -29
  376. package/dist/src/server/routes/search.js.map +1 -1
  377. package/dist/src/server/routes/triage.d.ts.map +1 -1
  378. package/dist/src/server/routes/triage.js +4 -1
  379. package/dist/src/server/routes/triage.js.map +1 -1
  380. package/dist/src/storage/rebuild.d.ts +35 -1
  381. package/dist/src/storage/rebuild.d.ts.map +1 -1
  382. package/dist/src/storage/rebuild.js +288 -64
  383. package/dist/src/storage/rebuild.js.map +1 -1
  384. package/dist/src/storage/tables.d.ts +29 -0
  385. package/dist/src/storage/tables.d.ts.map +1 -1
  386. package/dist/src/storage/tables.js +32 -1
  387. package/dist/src/storage/tables.js.map +1 -1
  388. package/operator/swiftbar/render-menu.py +524 -0
  389. package/operator/swiftbar/rift.10s.sh +176 -0
  390. package/package.json +9 -3
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import { z } from "zod";
12
12
  import { toJobStatusView } from "../../jobs/types.js";
13
+ import { enqueueJob } from "./enqueue.js";
13
14
  import { HISTORICAL_BACKFILL_CAPABILITY_HEADER, HISTORICAL_BACKFILL_OVERRIDE_HEADER, INTERNAL_HISTORICAL_TRIAGE_CODEX_ROUTE, CODEX_CLI_OVERRIDE_PROVIDER, hasHistoricalBackfillCapability, isHistoricalTriageIdempotencyKey, } from "../../providers/operator-overrides.js";
14
15
  const TriageItemSchema = z
15
16
  .object({
@@ -75,7 +76,9 @@ export function registerTriageRoute(server, queue, dataDir) {
75
76
  if (parsed.data.idempotency_key !== undefined) {
76
77
  opts.idempotency_key = parsed.data.idempotency_key;
77
78
  }
78
- const result = await queue.create("triage", opts);
79
+ const result = await enqueueJob(reply, queue, "triage", opts);
80
+ if (!result)
81
+ return;
79
82
  if ("conflict" in result) {
80
83
  return reply.code(409).send({ error: "conflict" });
81
84
  }
@@ -1 +1 @@
1
- {"version":3,"file":"triage.js","sourceRoot":"","sources":["../../../../src/server/routes/triage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"triage.js","sourceRoot":"","sources":["../../../../src/server/routes/triage.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,18 @@
1
1
  import type { Config } from "../config/schema.js";
2
2
  import type { EmbeddingProvider } from "../providers/types.js";
3
3
  import type { TableName } from "./tables.js";
4
+ /**
5
+ * Shared duplicate-resolution policy for conversation raw files.
6
+ *
7
+ * Both repair paths — reconcile and full rebuild — must pick the SAME winner
8
+ * when several raw files compete for one row id or idempotency_key, otherwise
9
+ * the canonical conversation row could flip depending on which tool last ran,
10
+ * which is corrosive to trust and debugging. Policy: most-recent raw wins,
11
+ * ordered by filename (`<timestamp>_<id>.json`, ISO-8601-derived → lexicographic
12
+ * == chronological). Compare basenames so the ordering is stable regardless of
13
+ * which source folder a file lives in. Sorts most-recent FIRST (descending).
14
+ */
15
+ export declare function compareRawRecencyDesc(aPath: string, bPath: string): number;
4
16
  export interface RebuildDeps {
5
17
  config: Config;
6
18
  embeddingProvider: EmbeddingProvider;
@@ -11,6 +23,15 @@ export interface RebuildDeps {
11
23
  export interface RebuildReport {
12
24
  tables_rebuilt: TableName[];
13
25
  rows_reindexed: number;
26
+ /**
27
+ * Conversation raw files skipped because a row with the same non-empty
28
+ * `idempotency_key` was already inserted into the rebuilt index under a
29
+ * different row id. Mirrors reconcile's `missing_skipped_duplicate_key`:
30
+ * without it, an orphan raw left on disk (e.g. one reconcile already
31
+ * skipped) would resurface as a duplicate every time the index is rebuilt
32
+ * from raw — rebuild groups by row id and would otherwise miss the dup.
33
+ */
34
+ conversations_skipped_duplicate_key: number;
14
35
  }
15
36
  /**
16
37
  * Full rebuild from raw disk state via shadow-table swap.
@@ -26,7 +47,20 @@ export interface ReembedOptions {
26
47
  config?: Config;
27
48
  localEmbeddingProvider?: EmbeddingProvider;
28
49
  }
29
- export declare function reembed(embeddingProvider: EmbeddingProvider, scope: ReindexScope, config?: Config, localEmbeddingProvider?: EmbeddingProvider): Promise<number>;
50
+ export declare function reembed(embeddingProvider: EmbeddingProvider, scope: ReindexScope, dataDir: string, config?: Config, localEmbeddingProvider?: EmbeddingProvider): Promise<number>;
51
+ /**
52
+ * Remove structured-doc rows whose source file no longer exists on disk.
53
+ * Scoped to filesystem-sourced rows (watched + scheduled_scan).
54
+ *
55
+ * Why: deletions between scheduled scans (or while a watcher is offline)
56
+ * leave ghost rows in the index, so search can return content that no
57
+ * longer exists. This pass closes that gap.
58
+ *
59
+ * Safety: skips rows whose owning source root is unreachable, so an
60
+ * unmounted drive cannot trigger mass deletion. If `config` is omitted
61
+ * the function still runs, but without the unmounted-root guard.
62
+ */
63
+ export declare function removeStructuredDocOrphans(dataDir: string, config?: Config): Promise<number>;
30
64
  export declare function extractIdFromFilename(filename: string): string | undefined;
31
65
  /**
32
66
  * Extract the ISO timestamp from a raw conversation filename.
@@ -1 +1 @@
1
- {"version":3,"file":"rebuild.d.ts","sourceRoot":"","sources":["../../../src/storage/rebuild.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAM/D,OAAO,KAAK,EAIV,SAAS,EACV,MAAM,aAAa,CAAC;AAMrB,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,sBAAsB,CAAC,EAAE,iBAAiB,CAAC;CAC5C;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,SAAS,EAAE,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAoB3E;AAuMD,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,eAAe,GAAG,WAAW,GAAG,iBAAiB,CAAC;AAErF,MAAM,WAAW,cAAc;IAC7B,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,KAAK,EAAE,YAAY,CAAC;IACpB,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sBAAsB,CAAC,EAAE,iBAAiB,CAAC;CAC5C;AAED,wBAAsB,OAAO,CAC3B,iBAAiB,EAAE,iBAAiB,EACpC,KAAK,EAAE,YAAY,EACnB,MAAM,CAAC,EAAE,MAAM,EACf,sBAAsB,CAAC,EAAE,iBAAiB,GACzC,OAAO,CAAC,MAAM,CAAC,CAcjB;AA4JD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG1E;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAa/E"}
1
+ {"version":3,"file":"rebuild.d.ts","sourceRoot":"","sources":["../../../src/storage/rebuild.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAW/D,OAAO,KAAK,EAIV,SAAS,EACV,MAAM,aAAa,CAAC;AAWrB;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,sBAAsB,CAAC,EAAE,iBAAiB,CAAC;CAC5C;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,SAAS,EAAE,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,mCAAmC,EAAE,MAAM,CAAC;CAC7C;AAED;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAgD3E;AA2TD,MAAM,MAAM,YAAY,GACpB,KAAK,GACL,eAAe,GACf,WAAW,GACX,iBAAiB,CAAC;AAEtB,MAAM,WAAW,cAAc;IAC7B,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,KAAK,EAAE,YAAY,CAAC;IACpB,uDAAuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sBAAsB,CAAC,EAAE,iBAAiB,CAAC;CAC5C;AAED,wBAAsB,OAAO,CAC3B,iBAAiB,EAAE,iBAAiB,EACpC,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,EACf,sBAAsB,CAAC,EAAE,iBAAiB,GACzC,OAAO,CAAC,MAAM,CAAC,CA2CjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAoDjB;AAyPD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG1E;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,GACf,IAAI,GAAG,SAAS,CAclB"}
@@ -9,12 +9,30 @@
9
9
  import fs from "node:fs";
10
10
  import path from "node:path";
11
11
  import { CONVERSATION_SOURCES } from "../providers/types.js";
12
+ import { RIFT_NONE_EMBEDDING_PROVIDER, isPlaceholderEmbeddingProvider, } from "../providers/placeholder-embed.js";
12
13
  import { walkDirectory } from "../ingestion/scanner.js";
13
14
  import { isSupported } from "../ingestion/extractor.js";
14
15
  import { buildIgnoredPaths } from "../ingestion/ignored-paths.js";
15
16
  import { fileId } from "../ingestion/indexer.js";
17
+ import { conversationContentFingerprint } from "../ingestion/inbox-core/conversation-fingerprint.js";
16
18
  import { createShadowSwap } from "./shadow-swap.js";
19
+ import { recordEmbed } from "../observability/embedding-events.js";
20
+ import { appendIndexEvent, recordIndexWrite, } from "../observability/index-events.js";
17
21
  const ALL_CONV_SOURCES = [...CONVERSATION_SOURCES, "inbox"];
22
+ /**
23
+ * Shared duplicate-resolution policy for conversation raw files.
24
+ *
25
+ * Both repair paths — reconcile and full rebuild — must pick the SAME winner
26
+ * when several raw files compete for one row id or idempotency_key, otherwise
27
+ * the canonical conversation row could flip depending on which tool last ran,
28
+ * which is corrosive to trust and debugging. Policy: most-recent raw wins,
29
+ * ordered by filename (`<timestamp>_<id>.json`, ISO-8601-derived → lexicographic
30
+ * == chronological). Compare basenames so the ordering is stable regardless of
31
+ * which source folder a file lives in. Sorts most-recent FIRST (descending).
32
+ */
33
+ export function compareRawRecencyDesc(aPath, bPath) {
34
+ return path.basename(bPath).localeCompare(path.basename(aPath));
35
+ }
18
36
  /**
19
37
  * Full rebuild from raw disk state via shadow-table swap.
20
38
  * Creates shadow tables for all four tables, populates them from
@@ -22,19 +40,45 @@ const ALL_CONV_SOURCES = [...CONVERSATION_SOURCES, "inbox"];
22
40
  */
23
41
  export async function fullRebuild(deps) {
24
42
  const swap = createShadowSwap(deps.dataDir);
25
- const report = { tables_rebuilt: [], rows_reindexed: 0 };
43
+ const report = {
44
+ tables_rebuilt: [],
45
+ rows_reindexed: 0,
46
+ conversations_skipped_duplicate_key: 0,
47
+ };
26
48
  const swaps = new Map();
27
- await populateStructuredDocsShadows(deps, swap, swaps, report);
28
- await populateConversationShadows(deps, swap, swaps, report);
29
- await populateDigestShadow(deps, swap, swaps, report);
49
+ // Per-table populate counts. Used to emit one post-commit "live"
50
+ // index-event per table so `index.last_update_at` is only credited
51
+ // after the swap actually installs the shadows. If `commitAllSwaps`
52
+ // throws, we never emit the live events and the lane correctly
53
+ // shows shadow-only successes (which `computeIndexHealth` ignores).
54
+ const populated = new Map();
55
+ await populateStructuredDocsShadows(deps, swap, swaps, populated, report);
56
+ await populateConversationShadows(deps, swap, swaps, populated, report);
57
+ await populateDigestShadow(deps, swap, swaps, populated, report);
30
58
  await swap.commitAllSwaps(swaps);
59
+ // Post-commit: credit the lane with one "live" event per swapped
60
+ // table. The shadow events stay in the lane for debugging but do
61
+ // not bump `last_update_at` (see computeIndexHealth).
62
+ const commitTs = new Date().toISOString();
63
+ for (const [tableName, rowCount] of populated) {
64
+ appendIndexEvent(deps.dataDir, {
65
+ ts: commitTs,
66
+ table: tableName,
67
+ pipeline: "backfill",
68
+ operation: "shadow_swap_commit",
69
+ outcome: "success",
70
+ row_count: rowCount,
71
+ ms: 0,
72
+ phase: "live",
73
+ });
74
+ }
31
75
  report.tables_rebuilt.push("structured_docs", "structured_docs_local", "conversations_hot", "conversations_cold", "digests");
32
76
  return report;
33
77
  }
34
78
  // ---------------------------------------------------------------------------
35
79
  // Populate shadow tables from raw state
36
80
  // ---------------------------------------------------------------------------
37
- async function populateStructuredDocsShadows(deps, swap, swaps, report) {
81
+ async function populateStructuredDocsShadows(deps, swap, swaps, populated, report) {
38
82
  const cloudShadow = await swap.createShadow("structured_docs");
39
83
  const localShadow = await swap.createShadow("structured_docs_local");
40
84
  swaps.set("structured_docs", cloudShadow);
@@ -75,7 +119,11 @@ async function populateStructuredDocsShadows(deps, swap, swaps, report) {
75
119
  const targetShadow = isLocal ? localShadow : cloudShadow;
76
120
  try {
77
121
  const content = fs.readFileSync(canonical, "utf-8");
78
- const embedding = await provider.embed(content);
122
+ const embedding = await recordEmbed(deps.dataDir, provider, {
123
+ pipeline: "backfill",
124
+ operation: "structured_doc_embedding",
125
+ input_count: 1,
126
+ }, () => provider.embed(content));
79
127
  const row = {
80
128
  id,
81
129
  source_path: canonical,
@@ -89,7 +137,17 @@ async function populateStructuredDocsShadows(deps, swap, swaps, report) {
89
137
  indexed_at: new Date().toISOString(),
90
138
  metadata: "{}",
91
139
  };
92
- await targetShadow.add([row]);
140
+ const targetTableName = isLocal
141
+ ? "structured_docs_local"
142
+ : "structured_docs";
143
+ await recordIndexWrite(deps.dataDir, {
144
+ table: targetTableName,
145
+ pipeline: "backfill",
146
+ operation: "structured_doc_upsert",
147
+ row_count: 1,
148
+ phase: "shadow",
149
+ }, () => targetShadow.add([row]));
150
+ populated.set(targetTableName, (populated.get(targetTableName) ?? 0) + 1);
93
151
  report.rows_reindexed++;
94
152
  }
95
153
  catch (err) {
@@ -98,60 +156,112 @@ async function populateStructuredDocsShadows(deps, swap, swaps, report) {
98
156
  }
99
157
  }
100
158
  }
101
- async function populateConversationShadows(deps, swap, swaps, report) {
159
+ async function populateConversationShadows(deps, swap, swaps, populated, report) {
102
160
  const hotShadow = await swap.createShadow("conversations_hot");
103
161
  const coldShadow = await swap.createShadow("conversations_cold");
104
162
  swaps.set("conversations_hot", hotShadow);
105
163
  swaps.set("conversations_cold", coldShadow);
106
164
  const cutoff = new Date();
107
165
  cutoff.setDate(cutoff.getDate() - (deps.config.compaction.age_threshold_days ?? 30));
166
+ // Durable dedup across the whole rebuild: a non-empty idempotency_key may
167
+ // only be inserted once. Mirrors reconcile's key-skip so a raw file that
168
+ // reconcile already declined to revive cannot reappear as a duplicate here.
169
+ const seenKeys = new Set();
170
+ // Group raw files by stable row id ACROSS ALL SOURCES first. Refresh-on-grow
171
+ // may leave more than one raw artifact for the same id on disk if pruning was
172
+ // interrupted; reinserting all of them duplicates the conversation. Keep only
173
+ // the most-recent raw per id (shared recency policy). Collecting globally —
174
+ // rather than per source folder — matters because `seenKeys` is global: if we
175
+ // sorted within each source folder, an older raw in an earlier folder could
176
+ // claim a key before a newer same-key raw in a later folder was ever seen.
177
+ const latestByRowId = new Map();
108
178
  for (const source of ALL_CONV_SOURCES) {
109
179
  const rawDir = path.join(deps.dataDir, "raw", "conversations", source);
110
180
  if (!fs.existsSync(rawDir))
111
181
  continue;
112
- const files = fs.readdirSync(rawDir).filter((f) => f.endsWith(".json"));
113
- for (const file of files) {
182
+ for (const file of fs.readdirSync(rawDir)) {
183
+ if (!file.endsWith(".json"))
184
+ continue;
114
185
  const id = extractIdFromFilename(file);
115
186
  if (!id)
116
187
  continue;
117
188
  const rawPath = path.join(rawDir, file);
118
- try {
119
- const raw = JSON.parse(fs.readFileSync(rawPath, "utf-8"));
120
- const summary = raw.summary ?? "";
121
- const content = raw.content ?? summary;
122
- const vec = await deps.embeddingProvider.embed(content || summary);
123
- const row = {
124
- id,
125
- content,
126
- summary,
127
- embedding: vec,
128
- source: raw.source ?? "claude_code",
129
- domain: raw.domain ?? "tech",
130
- intent: raw.intent ?? "build",
131
- quality: raw.quality ?? "medium",
132
- topics: JSON.stringify(raw.topics ?? []),
133
- decisions: JSON.stringify(raw.decisions ?? []),
134
- key_outputs: JSON.stringify(raw.key_outputs ?? []),
135
- indexed_at: new Date().toISOString(),
136
- idempotency_key: raw.idempotency_key ?? "",
137
- };
138
- const filenameTime = extractTimestampFromFilename(file);
139
- const rowTime = filenameTime ?? fs.statSync(rawPath).mtime;
140
- if (rowTime >= cutoff) {
141
- await hotShadow.add([row]);
142
- }
143
- else {
144
- await coldShadow.add([row]);
145
- }
146
- report.rows_reindexed++;
189
+ const prior = latestByRowId.get(id);
190
+ if (!prior || compareRawRecencyDesc(rawPath, prior) < 0) {
191
+ latestByRowId.set(id, rawPath);
147
192
  }
148
- catch (err) {
149
- process.stderr.write(`rebuild: failed to index conversation ${id}: ${err instanceof Error ? err.message : String(err)}\n`);
193
+ }
194
+ }
195
+ // Process most-recent raw first (global order) so that when two row ids share
196
+ // one idempotency_key the newest wins and older duplicates are skipped —
197
+ // identical winner to reconcile via the shared recency comparator.
198
+ const ordered = [...latestByRowId.entries()].sort(([, a], [, b]) => compareRawRecencyDesc(a, b));
199
+ for (const [id, rawPath] of ordered) {
200
+ const file = path.basename(rawPath);
201
+ try {
202
+ const raw = JSON.parse(fs.readFileSync(rawPath, "utf-8"));
203
+ const key = typeof raw.idempotency_key === "string" ? raw.idempotency_key : "";
204
+ if (key && seenKeys.has(key)) {
205
+ report.conversations_skipped_duplicate_key++;
206
+ continue;
150
207
  }
208
+ const summary = raw.summary ?? "";
209
+ const content = raw.content ?? summary;
210
+ const vec = await recordEmbed(deps.dataDir, deps.embeddingProvider, {
211
+ pipeline: "backfill",
212
+ operation: "conversation_embedding",
213
+ input_count: 1,
214
+ }, () => deps.embeddingProvider.embed(content || summary));
215
+ const fingerprint = typeof raw.conversation_fingerprint === "string" &&
216
+ raw.conversation_fingerprint.length > 0
217
+ ? raw.conversation_fingerprint
218
+ : content
219
+ ? conversationContentFingerprint(content)
220
+ : "";
221
+ const row = {
222
+ id,
223
+ content,
224
+ summary,
225
+ embedding: vec,
226
+ source: raw.source ?? "claude_code",
227
+ domain: raw.domain ?? "tech",
228
+ intent: raw.intent ?? "build",
229
+ quality: raw.quality ?? "medium",
230
+ topics: JSON.stringify(raw.topics ?? []),
231
+ decisions: JSON.stringify(raw.decisions ?? []),
232
+ key_outputs: JSON.stringify(raw.key_outputs ?? []),
233
+ indexed_at: new Date().toISOString(),
234
+ idempotency_key: raw.idempotency_key ?? "",
235
+ conversation_fingerprint: fingerprint,
236
+ // Metadata restored from raw → preserve its provider marker. Embedding
237
+ // recomputed here → marker reflects the provider that just ran.
238
+ metadata_provider: typeof raw.metadata_provider === "string" ? raw.metadata_provider : "",
239
+ embedding_provider: isPlaceholderEmbeddingProvider(deps.embeddingProvider)
240
+ ? RIFT_NONE_EMBEDDING_PROVIDER
241
+ : "",
242
+ };
243
+ const filenameTime = extractTimestampFromFilename(file);
244
+ const rowTime = filenameTime ?? fs.statSync(rawPath).mtime;
245
+ const targetTable = rowTime >= cutoff ? "conversations_hot" : "conversations_cold";
246
+ const targetShadow = rowTime >= cutoff ? hotShadow : coldShadow;
247
+ await recordIndexWrite(deps.dataDir, {
248
+ table: targetTable,
249
+ pipeline: "backfill",
250
+ operation: "conversation_upsert",
251
+ row_count: 1,
252
+ phase: "shadow",
253
+ }, () => targetShadow.add([row]));
254
+ if (key)
255
+ seenKeys.add(key);
256
+ populated.set(targetTable, (populated.get(targetTable) ?? 0) + 1);
257
+ report.rows_reindexed++;
258
+ }
259
+ catch (err) {
260
+ process.stderr.write(`rebuild: failed to index conversation ${id}: ${err instanceof Error ? err.message : String(err)}\n`);
151
261
  }
152
262
  }
153
263
  }
154
- async function populateDigestShadow(deps, swap, swaps, report) {
264
+ async function populateDigestShadow(deps, swap, swaps, populated, report) {
155
265
  const shadow = await swap.createShadow("digests");
156
266
  swaps.set("digests", shadow);
157
267
  const digestDir = path.join(deps.dataDir, "raw", "digests");
@@ -164,7 +274,11 @@ async function populateDigestShadow(deps, swap, swaps, report) {
164
274
  if (typeof raw.id !== "string" || !raw.id)
165
275
  continue;
166
276
  const summary = raw.summary ?? raw.content ?? "";
167
- const vec = await deps.embeddingProvider.embed(summary);
277
+ const vec = await recordEmbed(deps.dataDir, deps.embeddingProvider, {
278
+ pipeline: "backfill",
279
+ operation: "digest_embedding",
280
+ input_count: 1,
281
+ }, () => deps.embeddingProvider.embed(summary));
168
282
  const row = {
169
283
  id: raw.id,
170
284
  content: raw.content ?? "",
@@ -175,7 +289,14 @@ async function populateDigestShadow(deps, swap, swaps, report) {
175
289
  digest_type: raw.digest_type ?? "weekly",
176
290
  created_at: raw.created_at ?? new Date().toISOString(),
177
291
  };
178
- await shadow.add([row]);
292
+ await recordIndexWrite(deps.dataDir, {
293
+ table: "digests",
294
+ pipeline: "backfill",
295
+ operation: "digest_upsert",
296
+ row_count: 1,
297
+ phase: "shadow",
298
+ }, () => shadow.add([row]));
299
+ populated.set("digests", (populated.get("digests") ?? 0) + 1);
179
300
  report.rows_reindexed++;
180
301
  }
181
302
  catch (err) {
@@ -184,27 +305,86 @@ async function populateDigestShadow(deps, swap, swaps, report) {
184
305
  }
185
306
  }
186
307
  }
187
- export async function reembed(embeddingProvider, scope, config, localEmbeddingProvider) {
308
+ export async function reembed(embeddingProvider, scope, dataDir, config, localEmbeddingProvider) {
188
309
  let count = 0;
189
310
  if (scope === "all" || scope === "structured_docs" || scope === "documents") {
190
- count += await reembedStructuredDocs(embeddingProvider, config, localEmbeddingProvider);
311
+ count += await reembedStructuredDocs(embeddingProvider, dataDir, config, localEmbeddingProvider);
191
312
  if (localEmbeddingProvider) {
192
- count += await reembedLocalStructuredDocs(localEmbeddingProvider);
313
+ count += await reembedLocalStructuredDocs(localEmbeddingProvider, dataDir);
193
314
  }
194
315
  }
195
316
  if (scope === "all" || scope === "conversations") {
196
- count += await reembedTable("conversations_hot", embeddingProvider, (r) => r.content || r.summary, "indexed_at");
197
- count += await reembedTable("conversations_cold", embeddingProvider, (r) => r.content || r.summary, "indexed_at");
198
- count += await reembedTable("digests", embeddingProvider, (r) => r.summary || r.content, "created_at");
317
+ count += await reembedTable("conversations_hot", embeddingProvider, dataDir, "conversation_embedding", (r) => r.content || r.summary, "indexed_at");
318
+ count += await reembedTable("conversations_cold", embeddingProvider, dataDir, "conversation_embedding", (r) => r.content || r.summary, "indexed_at");
319
+ count += await reembedTable("digests", embeddingProvider, dataDir, "digest_embedding", (r) => r.summary || r.content, "created_at");
199
320
  }
200
321
  return count;
201
322
  }
323
+ /**
324
+ * Remove structured-doc rows whose source file no longer exists on disk.
325
+ * Scoped to filesystem-sourced rows (watched + scheduled_scan).
326
+ *
327
+ * Why: deletions between scheduled scans (or while a watcher is offline)
328
+ * leave ghost rows in the index, so search can return content that no
329
+ * longer exists. This pass closes that gap.
330
+ *
331
+ * Safety: skips rows whose owning source root is unreachable, so an
332
+ * unmounted drive cannot trigger mass deletion. If `config` is omitted
333
+ * the function still runs, but without the unmounted-root guard.
334
+ */
335
+ export async function removeStructuredDocOrphans(dataDir, config) {
336
+ const { getTable } = await import("./tables.js");
337
+ const tableNames = ["structured_docs", "structured_docs_local"];
338
+ let removed = 0;
339
+ for (const tableName of tableNames) {
340
+ let table;
341
+ try {
342
+ table = getTable(tableName);
343
+ }
344
+ catch {
345
+ continue;
346
+ }
347
+ const rows = (await table.query().toArray());
348
+ const rootReachable = new Map();
349
+ const isReachable = (root) => {
350
+ const cached = rootReachable.get(root);
351
+ if (cached !== undefined)
352
+ return cached;
353
+ const ok = fs.existsSync(root);
354
+ rootReachable.set(root, ok);
355
+ return ok;
356
+ };
357
+ for (const row of rows) {
358
+ if (row.source_type !== "filesystem_watched" &&
359
+ row.source_type !== "filesystem_scheduled_scan")
360
+ continue;
361
+ if (config) {
362
+ const owner = findOwningSource(row.source_path, config);
363
+ if (!owner)
364
+ continue;
365
+ if (!isReachable(owner.path))
366
+ continue;
367
+ }
368
+ if (fs.existsSync(row.source_path))
369
+ continue;
370
+ try {
371
+ await table.delete(`id = '${row.id}'`);
372
+ removed++;
373
+ process.stderr.write(`reindex: removed orphan ${tableName} row ${row.id} (source missing: ${row.source_path})\n`);
374
+ }
375
+ catch (err) {
376
+ process.stderr.write(`reindex: failed to remove orphan ${row.id} in ${tableName}: ${err instanceof Error ? err.message : String(err)}\n`);
377
+ }
378
+ }
379
+ }
380
+ return removed;
381
+ }
202
382
  /**
203
383
  * Re-embed structured_docs with per-source provider routing.
204
384
  * Rows whose source_path falls under a local-extraction source use
205
385
  * localEmbeddingProvider; everything else uses the cloud provider.
206
386
  */
207
- async function reembedStructuredDocs(cloudProvider, config, localProvider) {
387
+ async function reembedStructuredDocs(cloudProvider, dataDir, config, localProvider) {
208
388
  const { getTable } = await import("./tables.js");
209
389
  const table = getTable("structured_docs");
210
390
  const allRows = (await table.query().toArray());
@@ -212,10 +392,21 @@ async function reembedStructuredDocs(cloudProvider, config, localProvider) {
212
392
  for (const row of allRows) {
213
393
  try {
214
394
  const provider = resolveProviderForPath(row.source_path, cloudProvider, config, localProvider);
215
- const newEmbedding = await provider.embed(row.content);
395
+ const newEmbedding = await recordEmbed(dataDir, provider, {
396
+ pipeline: "reindex",
397
+ operation: "structured_doc_embedding",
398
+ input_count: 1,
399
+ }, () => provider.embed(row.content));
216
400
  const oldTimestamp = row.indexed_at;
217
401
  const newTimestamp = new Date().toISOString();
218
- await table.add([{ ...row, embedding: newEmbedding, indexed_at: newTimestamp }]);
402
+ await recordIndexWrite(dataDir, {
403
+ table: "structured_docs",
404
+ pipeline: "reindex",
405
+ operation: "structured_doc_upsert",
406
+ row_count: 1,
407
+ }, () => table.add([
408
+ { ...row, embedding: newEmbedding, indexed_at: newTimestamp },
409
+ ]));
219
410
  try {
220
411
  await table.delete(`id = '${row.id}' AND indexed_at = '${oldTimestamp}'`);
221
412
  count++;
@@ -224,7 +415,9 @@ async function reembedStructuredDocs(cloudProvider, config, localProvider) {
224
415
  try {
225
416
  await table.delete(`id = '${row.id}' AND indexed_at = '${newTimestamp}'`);
226
417
  }
227
- catch { /* duplicate remains — reconcile will repair */ }
418
+ catch {
419
+ /* duplicate remains — reconcile will repair */
420
+ }
228
421
  }
229
422
  }
230
423
  catch (err) {
@@ -237,17 +430,28 @@ async function reembedStructuredDocs(cloudProvider, config, localProvider) {
237
430
  * Re-embed all rows in structured_docs_local using the local provider.
238
431
  * Simpler than the cloud path: all rows use the same provider.
239
432
  */
240
- async function reembedLocalStructuredDocs(localProvider) {
433
+ async function reembedLocalStructuredDocs(localProvider, dataDir) {
241
434
  const { getTable } = await import("./tables.js");
242
435
  const table = getTable("structured_docs_local");
243
436
  const allRows = (await table.query().toArray());
244
437
  let count = 0;
245
438
  for (const row of allRows) {
246
439
  try {
247
- const newEmbedding = await localProvider.embed(row.content);
440
+ const newEmbedding = await recordEmbed(dataDir, localProvider, {
441
+ pipeline: "reindex",
442
+ operation: "structured_doc_local_embedding",
443
+ input_count: 1,
444
+ }, () => localProvider.embed(row.content));
248
445
  const oldTimestamp = row.indexed_at;
249
446
  const newTimestamp = new Date().toISOString();
250
- await table.add([{ ...row, embedding: newEmbedding, indexed_at: newTimestamp }]);
447
+ await recordIndexWrite(dataDir, {
448
+ table: "structured_docs_local",
449
+ pipeline: "reindex",
450
+ operation: "structured_doc_local_upsert",
451
+ row_count: 1,
452
+ }, () => table.add([
453
+ { ...row, embedding: newEmbedding, indexed_at: newTimestamp },
454
+ ]));
251
455
  try {
252
456
  await table.delete(`id = '${row.id}' AND indexed_at = '${oldTimestamp}'`);
253
457
  count++;
@@ -256,7 +460,9 @@ async function reembedLocalStructuredDocs(localProvider) {
256
460
  try {
257
461
  await table.delete(`id = '${row.id}' AND indexed_at = '${newTimestamp}'`);
258
462
  }
259
- catch { /* duplicate remains — reconcile will repair */ }
463
+ catch {
464
+ /* duplicate remains — reconcile will repair */
465
+ }
260
466
  }
261
467
  }
262
468
  catch (err) {
@@ -278,22 +484,31 @@ function resolveProviderForPath(sourcePath, cloudProvider, config, localProvider
278
484
  return localProvider;
279
485
  return cloudProvider;
280
486
  }
487
+ /**
488
+ * True when `file` is the same as `root` or lives beneath it. Uses
489
+ * `path.relative` so sibling paths that merely share a string prefix
490
+ * (e.g. `/docs/app-old` vs root `/docs/app`) are correctly rejected.
491
+ */
492
+ function isPathWithin(file, root) {
493
+ const rel = path.relative(root, file);
494
+ return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
495
+ }
281
496
  /**
282
497
  * Find the most-specific source root that owns a given file path.
283
- * Longest prefix wins, so nested roots are correctly attributed.
498
+ * Longest matching root wins, so nested roots are correctly attributed.
284
499
  */
285
500
  function findOwningSource(filePath, config) {
286
501
  let best;
287
502
  let bestLen = 0;
288
503
  for (const s of config.sources) {
289
- if (filePath.startsWith(s.path) && s.path.length > bestLen) {
504
+ if (isPathWithin(filePath, s.path) && s.path.length > bestLen) {
290
505
  best = s;
291
506
  bestLen = s.path.length;
292
507
  }
293
508
  }
294
509
  return best;
295
510
  }
296
- async function reembedTable(tableName, embeddingProvider, getText, timestampField) {
511
+ async function reembedTable(tableName, embeddingProvider, dataDir, operation, getText, timestampField) {
297
512
  const { getTable } = await import("./tables.js");
298
513
  const table = getTable(tableName);
299
514
  const allRows = (await table.query().toArray());
@@ -301,10 +516,17 @@ async function reembedTable(tableName, embeddingProvider, getText, timestampFiel
301
516
  for (const row of allRows) {
302
517
  try {
303
518
  const text = getText(row);
304
- const newEmbedding = await embeddingProvider.embed(text);
519
+ const newEmbedding = await recordEmbed(dataDir, embeddingProvider, { pipeline: "reindex", operation, input_count: 1 }, () => embeddingProvider.embed(text));
305
520
  const oldTimestamp = row[timestampField];
306
521
  const newTimestamp = new Date().toISOString();
307
- await table.add([{ ...row, embedding: newEmbedding, [timestampField]: newTimestamp }]);
522
+ await recordIndexWrite(dataDir, {
523
+ table: tableName,
524
+ pipeline: "reindex",
525
+ operation: `${operation.replace(/_embedding$/, "")}_upsert`,
526
+ row_count: 1,
527
+ }, () => table.add([
528
+ { ...row, embedding: newEmbedding, [timestampField]: newTimestamp },
529
+ ]));
308
530
  try {
309
531
  await table.delete(`id = '${row.id}' AND ${timestampField} = '${oldTimestamp}'`);
310
532
  count++;
@@ -313,7 +535,9 @@ async function reembedTable(tableName, embeddingProvider, getText, timestampFiel
313
535
  try {
314
536
  await table.delete(`id = '${row.id}' AND ${timestampField} = '${newTimestamp}'`);
315
537
  }
316
- catch { /* duplicate remains — reconcile will repair */ }
538
+ catch {
539
+ /* duplicate remains — reconcile will repair */
540
+ }
317
541
  }
318
542
  }
319
543
  catch (err) {