@neuralis/brain-core 0.1.0 → 0.2.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.
Files changed (401) hide show
  1. package/README.md +26 -35
  2. package/app/README.md +1 -1
  3. package/app/cards/DiffView.tsx +9 -7
  4. package/app/cards/README.md +1 -1
  5. package/app/cards/diffLine.tsx +2 -4
  6. package/app/filesystem/README.md +4 -4
  7. package/app/filesystem/api/README.md +3 -3
  8. package/app/filesystem/api/index.ts +137 -123
  9. package/app/filesystem/domain/types.ts +36 -5
  10. package/app/filesystem/hooks/README.md +5 -5
  11. package/app/filesystem/hooks/useDiscoverableSources.ts +4 -2
  12. package/app/filesystem/hooks/useFilesystemSSE.ts +31 -23
  13. package/app/filesystem/hooks/useGitChangesInventory.ts +4 -2
  14. package/app/filesystem/host/README.md +3 -2
  15. package/app/filesystem/host/installFilesystemHostComponents.tsx +22 -9
  16. package/app/filesystem/store/README.md +11 -7
  17. package/app/filesystem/store/filesystemStore.ts +454 -162
  18. package/app/filesystem/store/gitSlice.ts +6 -6
  19. package/app/filesystem/store/listSlice.ts +10 -6
  20. package/app/filesystem/store/searchSlice.ts +27 -4
  21. package/app/filesystem/ui/FileEditor.tsx +48 -35
  22. package/app/filesystem/ui/FileTree.tsx +10 -9
  23. package/app/filesystem/ui/FileTreeSearch.tsx +29 -2
  24. package/app/filesystem/ui/GitCommitChangesView.tsx +5 -3
  25. package/app/filesystem/ui/GitCommitGraph.tsx +6 -5
  26. package/app/filesystem/ui/GitControlPanel.tsx +14 -13
  27. package/app/filesystem/ui/GitDiffView.tsx +2 -1
  28. package/app/filesystem/ui/GitFileDiff.tsx +7 -6
  29. package/app/filesystem/ui/PendingChangesPanel.tsx +88 -28
  30. package/app/filesystem/ui/PendingReviewDialog.tsx +52 -24
  31. package/app/filesystem/ui/README.md +8 -7
  32. package/app/filesystem/ui/ReembedDialog.tsx +22 -9
  33. package/app/filesystem/ui/SourceDeleteConfirm.tsx +1 -1
  34. package/app/filesystem/ui/SourcesPanel.tsx +135 -42
  35. package/app/filesystem/ui/VectorHealthPanel.tsx +20 -4
  36. package/app/filesystem/ui/pendingChangesModel.ts +9 -5
  37. package/dist/app/chunks/{astro-LRAFGXNX.js → astro-B2J4AS2D.js} +1 -1
  38. package/dist/app/chunks/bash-KY6SAQTH.js +1 -0
  39. package/dist/app/chunks/c-OBOPYVZ7.js +1 -0
  40. package/dist/app/chunks/{chunk-5H7ZCOD3.js → chunk-2X3GNKJ3.js} +1 -1
  41. package/dist/app/chunks/{chunk-XIFGTNRM.js → chunk-JPJIVRFI.js} +1 -1
  42. package/dist/app/chunks/{chunk-RH3QY5LF.js → chunk-LEFIFWND.js} +1 -1
  43. package/dist/app/chunks/{chunk-G4RZ3UNX.js → chunk-N6RXX3PH.js} +1 -1
  44. package/dist/app/chunks/{chunk-QXXJ73P5.js → chunk-PZAW4HPB.js} +1 -1
  45. package/dist/app/chunks/{chunk-3QLE7Y4L.js → chunk-ZBYWWTO6.js} +1 -1
  46. package/dist/app/chunks/cpp-TVWVOTEQ.js +1 -0
  47. package/dist/app/chunks/css-MANZBYA4.js +1 -0
  48. package/dist/app/chunks/{elixir-M36XROA6.js → elixir-S725Z7EH.js} +1 -1
  49. package/dist/app/chunks/{elm-7BG6QAIK.js → elm-5WUUSTNB.js} +1 -1
  50. package/dist/app/chunks/graphql-UGOPR3WU.js +1 -0
  51. package/dist/app/chunks/html-F6FWTBSG.js +1 -0
  52. package/dist/app/chunks/java-ANIVV5H5.js +1 -0
  53. package/dist/app/chunks/javascript-IH374YWG.js +1 -0
  54. package/dist/app/chunks/json-WGGVVXSB.js +1 -0
  55. package/dist/app/chunks/jsx-OTXJQD32.js +1 -0
  56. package/dist/app/chunks/{latex-ZWK7SXB6.js → latex-QCZNITGN.js} +1 -1
  57. package/dist/app/chunks/lua-4CRZ64PU.js +1 -0
  58. package/dist/app/chunks/{nginx-2L6VRV3A.js → nginx-V4CYS7WR.js} +1 -1
  59. package/dist/app/chunks/{perl-PBUAXXK6.js → perl-BHTX7F5B.js} +1 -1
  60. package/dist/app/chunks/{php-QJX57PA6.js → php-AYGJ5WYZ.js} +1 -1
  61. package/dist/app/chunks/r-4YFCFA4B.js +1 -0
  62. package/dist/app/chunks/{ruby-RLOWMVPO.js → ruby-42NMJWAL.js} +1 -1
  63. package/dist/app/chunks/{scss-B5CAI3HR.js → scss-XD7VU2CU.js} +1 -1
  64. package/dist/app/chunks/sql-B55MH6JJ.js +1 -0
  65. package/dist/app/chunks/{svelte-GT3D4C44.js → svelte-WR3QPIJU.js} +1 -1
  66. package/dist/app/chunks/tsx-S73GAE2L.js +1 -0
  67. package/dist/app/chunks/typescript-NEWOAB5Z.js +1 -0
  68. package/dist/app/chunks/{vue-JOW62MHD.js → vue-CCDS4VZK.js} +1 -1
  69. package/dist/app/chunks/xml-UXKAGLDR.js +1 -0
  70. package/dist/app/chunks/yaml-UMWKDTK7.js +1 -0
  71. package/dist/app/host.js +46 -46
  72. package/dist/scripts/embeddingLockV3.d.ts +30 -0
  73. package/dist/scripts/embeddingLockV3.d.ts.map +1 -0
  74. package/dist/scripts/embeddingLockV3.js +100 -0
  75. package/dist/scripts/embeddingLockV3.js.map +1 -0
  76. package/dist/scripts/vectorProofV1.d.ts +90 -0
  77. package/dist/scripts/vectorProofV1.d.ts.map +1 -0
  78. package/dist/scripts/vectorProofV1.js +220 -0
  79. package/dist/scripts/vectorProofV1.js.map +1 -0
  80. package/dist/src/config/env.d.ts +21 -0
  81. package/dist/src/config/env.d.ts.map +1 -1
  82. package/dist/src/config/env.js +55 -0
  83. package/dist/src/config/env.js.map +1 -1
  84. package/dist/src/connectors/AppSourceConfigStore.d.ts +12 -2
  85. package/dist/src/connectors/AppSourceConfigStore.d.ts.map +1 -1
  86. package/dist/src/connectors/AppSourceConfigStore.js +48 -40
  87. package/dist/src/connectors/AppSourceConfigStore.js.map +1 -1
  88. package/dist/src/connectors/LocalDiskConnector.d.ts +11 -14
  89. package/dist/src/connectors/LocalDiskConnector.d.ts.map +1 -1
  90. package/dist/src/connectors/LocalDiskConnector.js +87 -107
  91. package/dist/src/connectors/LocalDiskConnector.js.map +1 -1
  92. package/dist/src/connectors/sourceConfigMutation.d.ts +11 -1
  93. package/dist/src/connectors/sourceConfigMutation.d.ts.map +1 -1
  94. package/dist/src/connectors/sourceConfigMutation.js +36 -3
  95. package/dist/src/connectors/sourceConfigMutation.js.map +1 -1
  96. package/dist/src/domain/chunker.js +8 -3
  97. package/dist/src/domain/chunker.js.map +1 -1
  98. package/dist/src/domain/healthTypes.d.ts +11 -0
  99. package/dist/src/domain/healthTypes.d.ts.map +1 -1
  100. package/dist/src/domain/mime.d.ts +1 -0
  101. package/dist/src/domain/mime.d.ts.map +1 -1
  102. package/dist/src/domain/mime.js +48 -0
  103. package/dist/src/domain/mime.js.map +1 -1
  104. package/dist/src/domain/pendingChange.d.ts +7 -0
  105. package/dist/src/domain/pendingChange.d.ts.map +1 -1
  106. package/dist/src/domain/pendingChange.js +11 -0
  107. package/dist/src/domain/pendingChange.js.map +1 -1
  108. package/dist/src/domain/sourceConfig.d.ts +7 -14
  109. package/dist/src/domain/sourceConfig.d.ts.map +1 -1
  110. package/dist/src/domain/sourceConfig.js +1 -13
  111. package/dist/src/domain/sourceConfig.js.map +1 -1
  112. package/dist/src/domain/sourcePackages.d.ts +11 -2
  113. package/dist/src/domain/sourcePackages.d.ts.map +1 -1
  114. package/dist/src/domain/syncTrigger.d.ts +2 -3
  115. package/dist/src/domain/syncTrigger.d.ts.map +1 -1
  116. package/dist/src/domain/syncTrigger.js +2 -3
  117. package/dist/src/domain/syncTrigger.js.map +1 -1
  118. package/dist/src/domain/syncTypes.d.ts +21 -4
  119. package/dist/src/domain/syncTypes.d.ts.map +1 -1
  120. package/dist/src/domain/syncTypes.js.map +1 -1
  121. package/dist/src/domain/text.d.ts +3 -0
  122. package/dist/src/domain/text.d.ts.map +1 -1
  123. package/dist/src/domain/text.js +5 -0
  124. package/dist/src/domain/text.js.map +1 -1
  125. package/dist/src/domain/types.d.ts +9 -0
  126. package/dist/src/domain/types.d.ts.map +1 -1
  127. package/dist/src/embeddings/clients/GeminiEmbedder.d.ts.map +1 -1
  128. package/dist/src/embeddings/clients/GeminiEmbedder.js +11 -5
  129. package/dist/src/embeddings/clients/GeminiEmbedder.js.map +1 -1
  130. package/dist/src/embeddings/clients/OllamaEmbedder.d.ts.map +1 -1
  131. package/dist/src/embeddings/clients/OllamaEmbedder.js +13 -6
  132. package/dist/src/embeddings/clients/OllamaEmbedder.js.map +1 -1
  133. package/dist/src/embeddings/clients/OpenAIEmbedder.d.ts.map +1 -1
  134. package/dist/src/embeddings/clients/OpenAIEmbedder.js +12 -9
  135. package/dist/src/embeddings/clients/OpenAIEmbedder.js.map +1 -1
  136. package/dist/src/embeddings/clients/QwenEmbedder.d.ts.map +1 -1
  137. package/dist/src/embeddings/clients/QwenEmbedder.js +12 -5
  138. package/dist/src/embeddings/clients/QwenEmbedder.js.map +1 -1
  139. package/dist/src/embeddings/clients/VoyageEmbedder.d.ts.map +1 -1
  140. package/dist/src/embeddings/clients/VoyageEmbedder.js +12 -5
  141. package/dist/src/embeddings/clients/VoyageEmbedder.js.map +1 -1
  142. package/dist/src/embeddings/factory.d.ts +2 -0
  143. package/dist/src/embeddings/factory.d.ts.map +1 -1
  144. package/dist/src/embeddings/factory.js +23 -3
  145. package/dist/src/embeddings/factory.js.map +1 -1
  146. package/dist/src/embeddings/lock.d.ts +22 -11
  147. package/dist/src/embeddings/lock.d.ts.map +1 -1
  148. package/dist/src/embeddings/lock.js +47 -29
  149. package/dist/src/embeddings/lock.js.map +1 -1
  150. package/dist/src/embeddings/retry.d.ts +5 -0
  151. package/dist/src/embeddings/retry.d.ts.map +1 -1
  152. package/dist/src/embeddings/retry.js +35 -0
  153. package/dist/src/embeddings/retry.js.map +1 -1
  154. package/dist/src/events/pending.d.ts +56 -0
  155. package/dist/src/events/pending.d.ts.map +1 -0
  156. package/dist/src/events/pending.js +108 -0
  157. package/dist/src/events/pending.js.map +1 -0
  158. package/dist/src/events/source.d.ts +60 -14
  159. package/dist/src/events/source.d.ts.map +1 -1
  160. package/dist/src/events/source.js +149 -31
  161. package/dist/src/events/source.js.map +1 -1
  162. package/dist/src/events/vector.d.ts +33 -0
  163. package/dist/src/events/vector.d.ts.map +1 -0
  164. package/dist/src/events/vector.js +57 -0
  165. package/dist/src/events/vector.js.map +1 -0
  166. package/dist/src/git/GitRunner.d.ts.map +1 -1
  167. package/dist/src/git/GitRunner.js +11 -8
  168. package/dist/src/git/GitRunner.js.map +1 -1
  169. package/dist/src/git/sourceGitControl.d.ts.map +1 -1
  170. package/dist/src/git/sourceGitControl.js +2 -1
  171. package/dist/src/git/sourceGitControl.js.map +1 -1
  172. package/dist/src/index.d.ts +5 -4
  173. package/dist/src/index.d.ts.map +1 -1
  174. package/dist/src/index.js +4 -3
  175. package/dist/src/index.js.map +1 -1
  176. package/dist/src/lifecycle.d.ts +20 -4
  177. package/dist/src/lifecycle.d.ts.map +1 -1
  178. package/dist/src/lifecycle.js +5 -1
  179. package/dist/src/lifecycle.js.map +1 -1
  180. package/dist/src/routes/health.js +6 -1
  181. package/dist/src/routes/health.js.map +1 -1
  182. package/dist/src/routes/pending.d.ts.map +1 -1
  183. package/dist/src/routes/pending.js +2 -1
  184. package/dist/src/routes/pending.js.map +1 -1
  185. package/dist/src/routes/sources.d.ts.map +1 -1
  186. package/dist/src/routes/sources.js +60 -11
  187. package/dist/src/routes/sources.js.map +1 -1
  188. package/dist/src/services/BridgeService.d.ts +2 -2
  189. package/dist/src/services/BridgeService.d.ts.map +1 -1
  190. package/dist/src/services/BridgeService.js +11 -2
  191. package/dist/src/services/BridgeService.js.map +1 -1
  192. package/dist/src/services/FilesystemService.d.ts +3 -1
  193. package/dist/src/services/FilesystemService.d.ts.map +1 -1
  194. package/dist/src/services/FilesystemService.js +3 -1
  195. package/dist/src/services/FilesystemService.js.map +1 -1
  196. package/dist/src/services/FilesystemUiService.js +1 -1
  197. package/dist/src/services/FilesystemUiService.js.map +1 -1
  198. package/dist/src/services/SyncScheduler.d.ts.map +1 -1
  199. package/dist/src/services/SyncScheduler.js +0 -14
  200. package/dist/src/services/SyncScheduler.js.map +1 -1
  201. package/dist/src/services/VectorHealthService.d.ts.map +1 -1
  202. package/dist/src/services/VectorHealthService.js +2 -46
  203. package/dist/src/services/VectorHealthService.js.map +1 -1
  204. package/dist/src/services/baselineRepair.d.ts +2 -2
  205. package/dist/src/services/baselineRepair.js +2 -2
  206. package/dist/src/services/declaredSources.d.ts +8 -1
  207. package/dist/src/services/declaredSources.d.ts.map +1 -1
  208. package/dist/src/services/declaredSources.js +19 -19
  209. package/dist/src/services/declaredSources.js.map +1 -1
  210. package/dist/src/store/dataFormats.d.ts +14 -3
  211. package/dist/src/store/dataFormats.d.ts.map +1 -1
  212. package/dist/src/store/dataFormats.js +15 -5
  213. package/dist/src/store/dataFormats.js.map +1 -1
  214. package/dist/src/sync/DeltaSyncRunner.d.ts.map +1 -1
  215. package/dist/src/sync/DeltaSyncRunner.js +78 -243
  216. package/dist/src/sync/DeltaSyncRunner.js.map +1 -1
  217. package/dist/src/sync/SyncJobQueue.d.ts +33 -4
  218. package/dist/src/sync/SyncJobQueue.d.ts.map +1 -1
  219. package/dist/src/sync/SyncJobQueue.js +179 -19
  220. package/dist/src/sync/SyncJobQueue.js.map +1 -1
  221. package/dist/src/sync/moveDetector.d.ts +2 -1
  222. package/dist/src/sync/moveDetector.d.ts.map +1 -1
  223. package/dist/src/sync/moveDetector.js +2 -1
  224. package/dist/src/sync/moveDetector.js.map +1 -1
  225. package/dist/src/tools/fs_search.d.ts.map +1 -1
  226. package/dist/src/tools/fs_search.js +8 -0
  227. package/dist/src/tools/fs_search.js.map +1 -1
  228. package/dist/src/tools/fs_write.js +3 -1
  229. package/dist/src/tools/fs_write.js.map +1 -1
  230. package/dist/src/usecases/chunkUpsert.d.ts +4 -0
  231. package/dist/src/usecases/chunkUpsert.d.ts.map +1 -1
  232. package/dist/src/usecases/chunkUpsert.js +103 -60
  233. package/dist/src/usecases/chunkUpsert.js.map +1 -1
  234. package/dist/src/usecases/embeddingBacklog.d.ts +31 -0
  235. package/dist/src/usecases/embeddingBacklog.d.ts.map +1 -0
  236. package/dist/src/usecases/embeddingBacklog.js +244 -0
  237. package/dist/src/usecases/embeddingBacklog.js.map +1 -0
  238. package/dist/src/usecases/listSourcePackages.js +17 -1
  239. package/dist/src/usecases/listSourcePackages.js.map +1 -1
  240. package/dist/src/usecases/mediaLane.d.ts +11 -4
  241. package/dist/src/usecases/mediaLane.d.ts.map +1 -1
  242. package/dist/src/usecases/mediaLane.js +67 -30
  243. package/dist/src/usecases/mediaLane.js.map +1 -1
  244. package/dist/src/usecases/pendingReview.d.ts.map +1 -1
  245. package/dist/src/usecases/pendingReview.js +24 -7
  246. package/dist/src/usecases/pendingReview.js.map +1 -1
  247. package/dist/src/usecases/purgeExcludedPoints.d.ts +2 -0
  248. package/dist/src/usecases/purgeExcludedPoints.d.ts.map +1 -1
  249. package/dist/src/usecases/purgeExcludedPoints.js +3 -2
  250. package/dist/src/usecases/purgeExcludedPoints.js.map +1 -1
  251. package/dist/src/usecases/readFiles.d.ts.map +1 -1
  252. package/dist/src/usecases/readFiles.js +33 -6
  253. package/dist/src/usecases/readFiles.js.map +1 -1
  254. package/dist/src/usecases/reconcileSourcePackageRoots.d.ts +31 -0
  255. package/dist/src/usecases/reconcileSourcePackageRoots.d.ts.map +1 -0
  256. package/dist/src/usecases/reconcileSourcePackageRoots.js +114 -0
  257. package/dist/src/usecases/reconcileSourcePackageRoots.js.map +1 -0
  258. package/dist/src/usecases/searchFiles.d.ts.map +1 -1
  259. package/dist/src/usecases/searchFiles.js +62 -64
  260. package/dist/src/usecases/searchFiles.js.map +1 -1
  261. package/dist/src/usecases/syncIndexedFile.d.ts +42 -0
  262. package/dist/src/usecases/syncIndexedFile.d.ts.map +1 -0
  263. package/dist/src/usecases/syncIndexedFile.js +242 -0
  264. package/dist/src/usecases/syncIndexedFile.js.map +1 -0
  265. package/dist/src/usecases/syncPointWrites.d.ts +5 -0
  266. package/dist/src/usecases/syncPointWrites.d.ts.map +1 -1
  267. package/dist/src/usecases/syncPointWrites.js +32 -5
  268. package/dist/src/usecases/syncPointWrites.js.map +1 -1
  269. package/dist/src/usecases/uploadBinary.d.ts.map +1 -1
  270. package/dist/src/usecases/uploadBinary.js +36 -49
  271. package/dist/src/usecases/uploadBinary.js.map +1 -1
  272. package/dist/src/usecases/vector/endpointGuard.js +2 -1
  273. package/dist/src/usecases/vector/endpointGuard.js.map +1 -1
  274. package/dist/src/usecases/vector/estimate.d.ts +7 -4
  275. package/dist/src/usecases/vector/estimate.d.ts.map +1 -1
  276. package/dist/src/usecases/vector/estimate.js +8 -5
  277. package/dist/src/usecases/vector/estimate.js.map +1 -1
  278. package/dist/src/usecases/vector/rebuildGeneration.d.ts +29 -5
  279. package/dist/src/usecases/vector/rebuildGeneration.d.ts.map +1 -1
  280. package/dist/src/usecases/vector/rebuildGeneration.js +224 -82
  281. package/dist/src/usecases/vector/rebuildGeneration.js.map +1 -1
  282. package/dist/src/usecases/vectorMaintenance.d.ts +46 -4
  283. package/dist/src/usecases/vectorMaintenance.d.ts.map +1 -1
  284. package/dist/src/usecases/vectorMaintenance.js +190 -11
  285. package/dist/src/usecases/vectorMaintenance.js.map +1 -1
  286. package/dist/src/usecases/write/commit.d.ts +6 -8
  287. package/dist/src/usecases/write/commit.d.ts.map +1 -1
  288. package/dist/src/usecases/write/commit.js +96 -112
  289. package/dist/src/usecases/write/commit.js.map +1 -1
  290. package/dist/src/usecases/write/content.js +1 -2
  291. package/dist/src/usecases/write/content.js.map +1 -1
  292. package/dist/src/usecases/write/ports.d.ts +1 -0
  293. package/dist/src/usecases/write/ports.d.ts.map +1 -1
  294. package/dist/src/vector/FilesystemVectorIndex.d.ts +34 -3
  295. package/dist/src/vector/FilesystemVectorIndex.d.ts.map +1 -1
  296. package/dist/src/vector/FilesystemVectorIndex.js +254 -20
  297. package/dist/src/vector/FilesystemVectorIndex.js.map +1 -1
  298. package/dist/src/vector/InMemoryVectorIndex.d.ts +9 -2
  299. package/dist/src/vector/InMemoryVectorIndex.d.ts.map +1 -1
  300. package/dist/src/vector/InMemoryVectorIndex.js +25 -6
  301. package/dist/src/vector/InMemoryVectorIndex.js.map +1 -1
  302. package/dist/src/vector/QdrantVectorIndex.d.ts +35 -9
  303. package/dist/src/vector/QdrantVectorIndex.d.ts.map +1 -1
  304. package/dist/src/vector/QdrantVectorIndex.js +209 -90
  305. package/dist/src/vector/QdrantVectorIndex.js.map +1 -1
  306. package/dist/src/vector/qdrantFilter.d.ts +3 -1
  307. package/dist/src/vector/qdrantFilter.d.ts.map +1 -1
  308. package/dist/src/vector/qdrantFilter.js +2 -0
  309. package/dist/src/vector/qdrantFilter.js.map +1 -1
  310. package/dist/src/vector/types.d.ts +81 -0
  311. package/dist/src/vector/types.d.ts.map +1 -1
  312. package/dist/src/vector/types.js +2 -0
  313. package/dist/src/vector/types.js.map +1 -1
  314. package/dist/src/wiring.d.ts +6 -0
  315. package/dist/src/wiring.d.ts.map +1 -1
  316. package/dist/src/wiring.js +62 -3
  317. package/dist/src/wiring.js.map +1 -1
  318. package/dist/tools/fs_search.json +2 -2
  319. package/docs/drive-overview.md +2 -2
  320. package/instructions/brain-model.md +1 -1
  321. package/package.json +1000 -11
  322. package/rules/brain-rules.md +1 -1
  323. package/skills/connect-business-data/SKILL.md +6 -4
  324. package/skills/create-package/SKILL.md +14 -12
  325. package/skills/create-package/references/config-and-credentials.md +3 -3
  326. package/skills/create-package/references/connectors-and-uri-policies.md +5 -3
  327. package/skills/create-package/references/manifest-and-discovery.md +5 -4
  328. package/skills/create-package/references/runtime-and-build.md +9 -9
  329. package/skills/create-package/references/skills-and-context.md +3 -3
  330. package/skills/create-package/references/surfaces-and-design.md +9 -6
  331. package/skills/create-package/references/team-and-workflows.md +2 -2
  332. package/skills/create-package/references/test-and-ship.md +15 -15
  333. package/skills/create-package/references/tools-and-schemas.md +1 -1
  334. package/skills/create-package/scripts/check-package.sh +50 -4
  335. package/skills/create-package/scripts/verify-loaded.sh +16 -16
  336. package/skills/manage-files/SKILL.md +2 -2
  337. package/skills/manage-sources/SKILL.md +16 -15
  338. package/skills/manage-sources/scripts/reembed.sh +9 -3
  339. package/skills/sync/SKILL.md +4 -4
  340. package/tools/fs_search.json +2 -2
  341. package/app/filesystem/hooks/useContainerWidth.ts +0 -39
  342. package/dist/app/chunks/bash-M45GHPBQ.js +0 -1
  343. package/dist/app/chunks/c-X3REOAWD.js +0 -1
  344. package/dist/app/chunks/cpp-VOWI743W.js +0 -1
  345. package/dist/app/chunks/css-2ZVPHE25.js +0 -1
  346. package/dist/app/chunks/graphql-7V2NP54C.js +0 -1
  347. package/dist/app/chunks/html-UF3LFLNA.js +0 -1
  348. package/dist/app/chunks/java-PNTAU4L3.js +0 -1
  349. package/dist/app/chunks/javascript-4JJTQGEL.js +0 -1
  350. package/dist/app/chunks/json-VLQ6D3JH.js +0 -1
  351. package/dist/app/chunks/jsx-KQKSLAAH.js +0 -1
  352. package/dist/app/chunks/lua-UFPYUJ2O.js +0 -1
  353. package/dist/app/chunks/r-QEZB45SR.js +0 -1
  354. package/dist/app/chunks/sql-DTZHNUZP.js +0 -1
  355. package/dist/app/chunks/tsx-WPR2WRZD.js +0 -1
  356. package/dist/app/chunks/typescript-LWO362OA.js +0 -1
  357. package/dist/app/chunks/xml-A5H72RTS.js +0 -1
  358. package/dist/app/chunks/yaml-MXHSIJCI.js +0 -1
  359. package/dist/src/services/purgePendingNodes.d.ts +0 -32
  360. package/dist/src/services/purgePendingNodes.d.ts.map +0 -1
  361. package/dist/src/services/purgePendingNodes.js +0 -61
  362. package/dist/src/services/purgePendingNodes.js.map +0 -1
  363. /package/dist/app/chunks/{chunk-737Y24HS.js → chunk-3JASFUPF.js} +0 -0
  364. /package/dist/app/chunks/{chunk-DKD2L57N.js → chunk-6EYLOQ4F.js} +0 -0
  365. /package/dist/app/chunks/{chunk-KYH5EHHY.js → chunk-7YRZ5LOQ.js} +0 -0
  366. /package/dist/app/chunks/{chunk-6TC3DVWV.js → chunk-C7LU4RID.js} +0 -0
  367. /package/dist/app/chunks/{chunk-TY33BMKR.js → chunk-CD52YDZP.js} +0 -0
  368. /package/dist/app/chunks/{chunk-OLEIKJ3X.js → chunk-DCJKTCJ5.js} +0 -0
  369. /package/dist/app/chunks/{chunk-QLBZ4JR2.js → chunk-EXIU2KBV.js} +0 -0
  370. /package/dist/app/chunks/{chunk-SFUNBBEA.js → chunk-FDP4LO4P.js} +0 -0
  371. /package/dist/app/chunks/{chunk-BGRVFATT.js → chunk-HNETUS7U.js} +0 -0
  372. /package/dist/app/chunks/{chunk-4QNMHAIR.js → chunk-LBFZJEDR.js} +0 -0
  373. /package/dist/app/chunks/{chunk-TRRUHDPN.js → chunk-ULRXL5VM.js} +0 -0
  374. /package/dist/app/chunks/{chunk-S6B4WWIF.js → chunk-XJK47SMJ.js} +0 -0
  375. /package/dist/app/chunks/{chunk-DZ5C5FKI.js → chunk-YYAAIAU2.js} +0 -0
  376. /package/dist/app/chunks/{clojure-OC6KPSJO.js → clojure-4QCN4GHG.js} +0 -0
  377. /package/dist/app/chunks/{core-KGE7TQJ3.js → core-ZHTX3P2Q.js} +0 -0
  378. /package/dist/app/chunks/{csharp-E7VIFITL.js → csharp-HQWR4N52.js} +0 -0
  379. /package/dist/app/chunks/{dark-plus-BOSCZXE4.js → dark-plus-GYPKKZES.js} +0 -0
  380. /package/dist/app/chunks/{dart-6Y3X27SZ.js → dart-FO4TTVO2.js} +0 -0
  381. /package/dist/app/chunks/{diff-TR2NPA64.js → diff-SHANV4YC.js} +0 -0
  382. /package/dist/app/chunks/{dist-T32U2VVR.js → dist-WSDF34DM.js} +0 -0
  383. /package/dist/app/chunks/{docker-N33HOW54.js → docker-VX55HTDS.js} +0 -0
  384. /package/dist/app/chunks/{go-6DGG5UTP.js → go-5UCPEC6S.js} +0 -0
  385. /package/dist/app/chunks/{haskell-KK36PMMM.js → haskell-25PJ3OSG.js} +0 -0
  386. /package/dist/app/chunks/{ini-VHVQ5TPB.js → ini-BJIVD5MK.js} +0 -0
  387. /package/dist/app/chunks/{kotlin-44WS3JHE.js → kotlin-MNR6XOEQ.js} +0 -0
  388. /package/dist/app/chunks/{less-5WZ3KLAK.js → less-KA5JURX6.js} +0 -0
  389. /package/dist/app/chunks/{light-plus-HGGBC43G.js → light-plus-QXH2UUWZ.js} +0 -0
  390. /package/dist/app/chunks/{make-X7RXMVW7.js → make-LPD5KOCO.js} +0 -0
  391. /package/dist/app/chunks/{markdown-46IKUAIB.js → markdown-LMWF6MQR.js} +0 -0
  392. /package/dist/app/chunks/{mdx-DGLHDAO7.js → mdx-WPMNN5BS.js} +0 -0
  393. /package/dist/app/chunks/{nix-Q6FYQZLX.js → nix-QGWLUUMZ.js} +0 -0
  394. /package/dist/app/chunks/{powershell-L4KJJ6DK.js → powershell-7GSBBEFF.js} +0 -0
  395. /package/dist/app/chunks/{prisma-IKS4LVH2.js → prisma-FPMPQISO.js} +0 -0
  396. /package/dist/app/chunks/{proto-AYCDOTJO.js → proto-7KDX5Z2U.js} +0 -0
  397. /package/dist/app/chunks/{python-KXMCSAFQ.js → python-CFG4FTQW.js} +0 -0
  398. /package/dist/app/chunks/{rust-J3BC3P6V.js → rust-X7A37WU3.js} +0 -0
  399. /package/dist/app/chunks/{scala-AYIIHBJK.js → scala-OYHJSUG2.js} +0 -0
  400. /package/dist/app/chunks/{swift-QPHHCQD3.js → swift-PE7VNYW5.js} +0 -0
  401. /package/dist/app/chunks/{toml-UV7JK7K7.js → toml-ETQRDILP.js} +0 -0
@@ -8,7 +8,7 @@
8
8
  * per record, a refused revert included — nothing fails silently.
9
9
  */
10
10
 
11
- import React, { useCallback, useEffect, useMemo, useState } from 'react';
11
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
12
12
  import { createPortal } from 'react-dom';
13
13
  import { Check, CheckCheck, Loader2, ShieldAlert, Undo2, X, XCircle } from 'lucide-react';
14
14
  import {
@@ -18,7 +18,9 @@ import {
18
18
  import { DiffView } from '../../cards/DiffView';
19
19
  import { detectLanguage } from '../../cards/diffUtils';
20
20
  import type { VisibleDiffQueue } from '../hooks/useVisibleDiffCards';
21
- import { classifyReviewOutcome, hunksOf, type ReviewOutcome } from './pendingChangesModel';
21
+ import { acceptsAbsence, classifyReviewOutcome, hunksOf, type ReviewOutcome } from './pendingChangesModel';
22
+ import { useFilesystemStore } from '../store/filesystemStore';
23
+ import type { PackageApi } from '@neuralis/package-system/client';
22
24
 
23
25
  export type ReviewAction = 'approve' | 'dismiss' | 'revert';
24
26
  export type ReviewRequest =
@@ -34,7 +36,7 @@ const REASONS: Record<Reason, { label: string; chip: string }> = {
34
36
  };
35
37
  const OUTCOME: Record<ReviewOutcome, { label: string; tone: string }> = {
36
38
  ok: { label: 'Done', tone: 'text-emerald-300' },
37
- changed: { label: 'Changed since the preview — reopen it', tone: 'text-amber-300' },
39
+ changed: { label: 'Changed since the preview — check the fresh state and confirm again', tone: 'text-amber-300' },
38
40
  'needs-ack': { label: 'Still needs a confirmation', tone: 'text-amber-300' },
39
41
  error: { label: 'Refused', tone: 'text-rose-300' },
40
42
  };
@@ -42,13 +44,13 @@ const OUTCOME: Record<ReviewOutcome, { label: string; tone: string }> = {
42
44
  export type ReviewResult = { id: string; uri: string; action: ReviewAction; outcome: ReviewOutcome; detail?: string };
43
45
 
44
46
  /** Run one review verb with the record's own fingerprint; never throws. */
45
- export async function applyReview(params: { agentId: string; projectId: string | null }, rec: PendingChangeRecord,
47
+ export async function applyReview(params: { agentId: string; projectId: string | null; client: PackageApi }, rec: PendingChangeRecord,
46
48
  preview: PendingPreview, action: ReviewAction, acknowledge: boolean): Promise<ReviewResult> {
47
49
  const body = { agentId: params.agentId, ...(params.projectId ? { projectId: params.projectId } : {}), id: rec.id,
48
50
  expectedFingerprint: preview.expectedFingerprint, ...(acknowledge ? { acknowledgeCurrentState: true as const } : {}) };
49
51
  try {
50
- const result = action === 'approve' ? await approveFileReview({ ...body, uri: rec.uri })
51
- : action === 'revert' ? await revertPendingChange(body) : await dismissPendingChange(body);
52
+ const result = action === 'approve' ? await approveFileReview({ ...body, uri: rec.uri }, params.client)
53
+ : action === 'revert' ? await revertPendingChange(body, params.client) : await dismissPendingChange(body, params.client);
52
54
  if (result && result.ok === false) {
53
55
  const detail = 'error' in result && result.error ? String(result.error) : 'Review refused';
54
56
  return { id: rec.id, uri: rec.uri, action, outcome: classifyReviewOutcome(detail), detail };
@@ -62,6 +64,12 @@ export async function applyReview(params: { agentId: string; projectId: string |
62
64
 
63
65
  function PreviewBody({ rec, preview }: { rec: PendingChangeRecord; preview: PendingPreview }) {
64
66
  const hunks = useMemo(() => hunksOf(preview), [preview]);
67
+ if (acceptsAbsence(rec, preview)) {
68
+ return <div className="px-2.5 py-2 text-[10.5px] text-white/60 leading-relaxed">
69
+ The file is gone. Approving or dismissing accepts that it stays gone — nothing is recreated and the saved
70
+ earlier version stays in history. Revert restores that version.
71
+ </div>;
72
+ }
65
73
  if (!hunks) {
66
74
  return <div className="px-2.5 py-2 text-[10.5px] text-white/50">
67
75
  Diff {preview.diffState}: current {preview.current.state}, baseline {preview.baseline.state} ({preview.baselineProvenance}).
@@ -74,30 +82,42 @@ function PreviewBody({ rec, preview }: { rec: PendingChangeRecord; preview: Pend
74
82
 
75
83
  const BUTTON = 'h-7 px-2.5 rounded-md text-[11px] font-medium transition disabled:opacity-40 inline-flex items-center gap-1.5';
76
84
 
77
- export function PendingReviewDialog({ request, agentId, projectId, run, onClose, onDone, next = 0, onNext }: {
85
+ export function PendingReviewDialog({ request, agentId, projectId, run, onClose, onDone, next = 0, onNext, refetch, onBatchStart }: {
78
86
  request: ReviewRequest; agentId: string; projectId: string | null; run: VisibleDiffQueue['run'];
87
+ /** The post-409 refetch, when the host owns it (the panel's card then reuses this ONE answer). */
88
+ refetch?: (rec: PendingChangeRecord) => Promise<PendingPreview>;
89
+ /** A batch starts writing: the host holds its revision-driven re-reads until `onDone`. */
90
+ onBatchStart?: () => void;
79
91
  onClose: () => void; onDone: (results: ReviewResult[]) => void;
80
92
  /** Records still waiting outside this batch; `onNext` (given this batch's ids) opens the next one. */
81
93
  next?: number; onNext?: (batch: string[]) => void;
82
94
  }): React.ReactElement | null {
95
+ const client = useFilesystemStore((s) => s.client);
83
96
  const records = useMemo(() => request.kind === 'single' ? [request.rec] : request.records, [request]);
84
97
  const [previews, setPreviews] = useState<Record<string, PendingPreview | Error>>(
85
98
  () => request.kind === 'single' && request.preview ? { [request.rec.id]: request.preview } : {});
86
99
  const [busy, setBusy] = useState(false);
87
100
  const [results, setResults] = useState<Record<string, ReviewResult>>({});
101
+ // Liveness is cleared on UNMOUNT only; a per-record sequence drops a superseded answer.
102
+ const mounted = useRef(true);
103
+ const loads = useRef(new Map<string, number>());
104
+ useEffect(() => { mounted.current = true; return () => { mounted.current = false; }; }, []);
105
+
106
+ const loadPreview = useCallback((rec: PendingChangeRecord, again = false) => {
107
+ const seq = (loads.current.get(rec.id) ?? 0) + 1;
108
+ loads.current.set(rec.id, seq);
109
+ const current = () => mounted.current && loads.current.get(rec.id) === seq;
110
+ const load = () => previewPendingChange({ agentId, ...(projectId ? { projectId } : {}), id: rec.id }, client);
111
+ void (again && refetch ? refetch(rec) : run(load, current))
112
+ .then(preview => { if (current() && preview) setPreviews(p => ({ ...p, [rec.id]: preview })); },
113
+ error => { if (current()) setPreviews(p => ({ ...p, [rec.id]: error instanceof Error ? error : new Error('Preview failed') })); });
114
+ }, [agentId, projectId, run, refetch]);
88
115
 
89
116
  useEffect(() => {
90
- let live = true;
91
- for (const rec of records) {
92
- if (previews[rec.id]) continue;
93
- void run(() => previewPendingChange({ agentId, ...(projectId ? { projectId } : {}), id: rec.id }), () => live)
94
- .then(preview => { if (live && preview) setPreviews(p => ({ ...p, [rec.id]: preview })); },
95
- error => { if (live) setPreviews(p => ({ ...p, [rec.id]: error instanceof Error ? error : new Error('Preview failed') })); });
96
- }
97
- return () => { live = false; };
117
+ for (const rec of records) if (!previews[rec.id] && !loads.current.has(rec.id)) loadPreview(rec);
98
118
  // Previews are fetched once per opened request; `previews` is read, not tracked.
99
119
  // eslint-disable-next-line react-hooks/exhaustive-deps
100
- }, [records, agentId, projectId, run]);
120
+ }, [records, loadPreview]);
101
121
 
102
122
  const groups = useMemo(() => {
103
123
  const out = new Map<Reason, PendingChangeRecord[]>();
@@ -110,24 +130,32 @@ export function PendingReviewDialog({ request, agentId, projectId, run, onClose,
110
130
  return out;
111
131
  }, [records, previews]);
112
132
  const pending = records.filter(rec => !previews[rec.id]).length;
113
- // A batch is finished once every record has an outcome (or a failed preview); its
114
- // group buttons stop accepting clicks and the footer offers the next batch.
133
+ // A batch is finished once every record has a final outcome (or a failed preview);
134
+ // `changed` is not final — its fresh preview waits for a new click. Its group
135
+ // buttons stop accepting clicks and the footer offers the next batch.
136
+ const settled = (rec: PendingChangeRecord) => results[rec.id] !== undefined && results[rec.id].outcome !== 'changed';
115
137
  const finished = request.kind === 'group' && pending === 0 && !busy
116
- && records.every(rec => results[rec.id] || previews[rec.id] instanceof Error);
138
+ && records.every(rec => settled(rec) || previews[rec.id] instanceof Error);
117
139
 
118
140
  const execute = useCallback(async (targets: PendingChangeRecord[], action: ReviewAction) => {
119
- setBusy(true);
141
+ setBusy(true); onBatchStart?.();
120
142
  const done: ReviewResult[] = [];
121
143
  for (const rec of targets) {
122
144
  const preview = previews[rec.id];
123
145
  if (!preview || preview instanceof Error || results[rec.id]?.outcome === 'ok') continue;
124
- const result = await applyReview({ agentId, projectId }, rec, preview, action, preview.requiresAcknowledgement.length > 0);
146
+ const result = await applyReview({ agentId, projectId, client }, rec, preview, action, preview.requiresAcknowledgement.length > 0);
125
147
  done.push(result);
126
148
  setResults(r => ({ ...r, [rec.id]: result }));
149
+ // The state moved under the preview: fetch ONE fresh preview for this record
150
+ // and wait for a new click — a refused action is never replayed by itself.
151
+ if (result.outcome === 'changed') {
152
+ setPreviews(p => { const { [rec.id]: _stale, ...rest } = p; return rest; });
153
+ loadPreview(rec, true);
154
+ }
127
155
  }
128
156
  setBusy(false);
129
157
  onDone(done);
130
- }, [previews, results, agentId, projectId, onDone]);
158
+ }, [previews, results, agentId, projectId, onDone, loadPreview, onBatchStart]);
131
159
 
132
160
  if (typeof document === 'undefined') return null;
133
161
  const single = request.kind === 'single' ? request : null;
@@ -160,11 +188,11 @@ export function PendingReviewDialog({ request, agentId, projectId, run, onClose,
160
188
  {REASONS[reason].label} · {recs.length}
161
189
  </span>
162
190
  {!single ? <div className="ml-auto flex items-center gap-1.5">
163
- <button type="button" disabled={busy || recs.every(rec => results[rec.id])} onClick={() => void execute(recs, 'dismiss')}
191
+ <button type="button" disabled={busy || recs.every(settled)} onClick={() => void execute(recs, 'dismiss')}
164
192
  className={`${BUTTON} bg-white/[0.04] hover:bg-white/[0.08] text-white/70`}>
165
193
  <XCircle className="h-3 w-3" /> Confirm &amp; dismiss {recs.length}
166
194
  </button>
167
- <button type="button" disabled={busy || recs.every(rec => results[rec.id])} onClick={() => void execute(recs, 'approve')}
195
+ <button type="button" disabled={busy || recs.every(settled)} onClick={() => void execute(recs, 'approve')}
168
196
  className={`${BUTTON} bg-emerald-500/20 hover:bg-emerald-500/30 border border-emerald-400/40 text-emerald-100 font-semibold`}>
169
197
  <CheckCheck className="h-3 w-3" /> Confirm &amp; approve {recs.length}
170
198
  </button>
@@ -14,9 +14,9 @@ View components for the Files widget: tree, editor, sources panel, packages pane
14
14
  Re-exported from `app/filesystem/ui/index.ts`:
15
15
 
16
16
  - `FilesystemWidget` — top-level widget; takes `{ agentId, widgetId, state? }`.
17
- - `FileTree`, `FileTreeNode`, `FileTreeSearch`, `SourceBranchHeader` — virtualised tree components (uses `@tanstack/react-virtual`). `FileTreeSearch` is the search PANEL body only (mode selector `keyword|semantic|grep`, filter row, results) — the query input rides inline in the sidebar HEADER (`FilesystemWidget`), panel visibility keys on `sidebarView === 'search'` alone (the header toggle/Escape clears + closes even with text), and the ≥2-char gate inside the panel only switches results-vs-hint.
17
+ - `FileTree`, `FileTreeNode`, `FileTreeSearch`, `SourceBranchHeader` — virtualised tree components (uses `@tanstack/react-virtual`). `FileTreeSearch` is the search PANEL body only (mode selector `keyword|semantic|grep`, filter row, results) — the query input rides inline in the sidebar HEADER (`FilesystemWidget`), panel visibility keys on `sidebarView === 'search'` alone (the header toggle/Escape clears + closes even with text), and the ≥2-char gate inside the panel only switches results-vs-hint. The store keeps the answer's `requested_mode`/`effective_mode`/`embedding_degraded`/`warning` as `searchEffect` (a `contextResetPatch` key) and the panel prints ONE `searchEffectLine` ("Searched as keyword — semantic search unavailable (<reason>)") plus an `embedding missing|stale` badge per row.
18
18
  - `FileTreeContextMenu`, `TreeDeleteConfirm` — the VS Code-style file-management surface (see the section below).
19
- - `ReembedDialog` — the ONE "Re-embed…" dialog, shared by the Sources panel (a whole source) and the tree's context menu (a folder or a file): opening it asks `sources/reindex` for a sample-based ESTIMATE only (no queue job, no sync-marker change); the forced re-embed with the active model starts on its Re-embed button and nowhere else. Portaled like `TreeDeleteConfirm`.
19
+ - `ReembedDialog` — the ONE estimate-first embed dialog, shared by the Sources panel (a whole source) and the tree's context menu (a folder or a file): opening it asks `sources/reindex` for a sample-based ESTIMATE only (no queue job, no sync-marker change); the work starts on its button and nowhere else. `kind="reembed"` (default) = the forced re-embed of every file with the active model; `kind="pending"` = **Embed pending…**, the `embeddingOnly` debt pass (only missing/stale embeddings; the estimate is the scope's upper bound). Portaled like `TreeDeleteConfirm`.
20
20
  - `DiscoverDock` — the **Discover** list of attachable-but-unattached sources (`GET /sources/discoverable`), pinned below the tree. **It is a SIBLING of the virtualized scroller, never a `FlatItemKind`**: a sixth item kind would have to join the `rowEstimate`/`isMeasuredRow` contract for a list that is never long enough to need virtualizing. Two placements, one component — COLLAPSED it sits in normal flow below the scroller (takes height, covers nothing, bounded to 5 rows); EXPANDED the tree renders it as an `absolute bottom-0` overlay, the ONLY state in which it may hide tree rows. Never both, so the scroller's height cannot change while the overlay is up. Visible only while EVERY source branch is collapsed (`sourceBranchExpanded`, the React-local BRANCH state — not the store's `expandedFolders`) and only for a caller holding `drive.mount`. **Consequence to know:** `activeFile` is persisted and `FileTree`'s auto-expand opens its source branch, so a returning user with an open tab lands with the dock hidden until they collapse that branch. Clicking a row hands the whole suggestion to the Sources panel through the store (`pendingSourceSuggestion`) and opens it — the prefill body lives in `SourcesPanel.applySuggestion`, ONE body shared with the panel's own Discoverable list.
21
21
  - `FileEditor`, `FileTab`, `FileMetaBar`, `CodeEditor`, `ReviewDiffView`, `EditorTabBar`, `DiffModeToggle` — the **VS Code-like editor stack**.
22
22
  - **`CodeEditor`** (CodeMirror 6 + an in-repo Shiki bridge under `codeEditor/`) is the ONE editor instance behind BOTH the read view (`readOnly`) and edit — toggling Edit only flips the `readOnly` Compartment, so the `EditorView` never remounts (no flash / scroll loss / re-tokenize). Syntax colors are the exact VS Code `dark-plus` TextMate grammars via Shiki's **JavaScript regex engine** — so **no web worker, no `.wasm`, no host CSP change** (`codeEditor/shikiHighlighter.ts` = the memoized core + `tokenizeLines` for the git-diff path — the core, the engine, both themes and all 50 `@shikijs/langs` grammars are dynamic imports, one split chunk each, so nothing of Shiki is on the module's cold path; the 15 base grammars load WITH the core so a markdown fence in one of them colours at once, the rest on first use; `codeEditor/shikiChunks.ts` = the INCREMENTAL tokenizer — position-anchored, line-aligned chunks closed at 64 lines OR 4 KB of text (whichever first) with TextMate grammar-state continuation, chunk-relative token offsets, the visible chunks tokenized synchronously under a 16 KB per-paint character budget (CodeMirror's `visibleRanges` right after a whole-document replace spans thousands of unmeasured lines — the budget, not the viewport estimate, bounds the sync path) and the prefix + a 4-chunk horizon after the viewport in idle slices (a slice is at least one chunk: measured ~90 ms worst case on dense TS/TSX and on long-line markdown, ~50 ms typical code, ~2–5 ms short-line prose — chunks past the horizon wait until scrolled to, so opening a file costs the viewport, not the document), a keystroke re-tokenizing its own chunk and walking forward only until the grammar state converges, a far scroll coloured provisionally first and corrected by the idle pass, and a whole-document replace (every file switch / reload / revert is one) split back into chunks eagerly so the first paint never tokenizes the whole file; `codeEditor/shikiDecorations.ts` = the per-view `ViewPlugin`→`DecorationSet` bridge that owns one engine per editor, because `@shikijs/codemirror` does not exist). The 120k wall is gone; `MAX_HIGHLIGHT_CHARS` (2 MB) is a memory guard on the document (above it the file renders plain), not a tokenization budget. Shared 12px/18px base theme (`codeEditor/editorTheme.ts`).
@@ -27,7 +27,7 @@ Re-exported from `app/filesystem/ui/index.ts`:
27
27
  - `DiffView` (the old hand-rolled Myers review diff) was **DELETED** — superseded by `ReviewDiffView`. NB `app/cards/DiffView.tsx` is a DIFFERENT file (the chat-card single-column diff) and stays.
28
28
  - `Toolbar` — top toolbar with the active agent’s standalone configured icon (no visible name or identity badge), sidebar toggle, refresh, upload, and panel switches. The Changes switch carries the caller's visible pending COUNT as a static badge (`pendingCount`, the server `count_only` answer — `1000+` at the listing cap); the client `pendingReviewPaths` set only tones tree rows.
29
29
  - `SourcesPanel`, `PackagesPanel`, `VectorHealthPanel`, `PanelScaffold` — right-hand panels. Sources offers advisory metrics exclusions only when enabling an attested own data source with no sync block; Save persists the editable draft. Media summaries include local structural PNG failures, and chunk-only cleanup refreshes Files separately from removed-file counts.
30
- - `PendingChangesPanel` — Changes groups current Git status and pending review by source/repository, including non-Git sources. Normalized URI joining renders a file once while retaining separately labeled staged/unstaged comparisons for each overlapping repository and one pending review. All cards start expanded in ONE `useVirtualizer` list (`pendingChangesModel.ts` owns the filters, the flat row list, `rowEstimate`/`isMeasuredRow` and the Approve split; the compact row pins `h-8`, every card is measured; `overflow-anchor:none` plus an above-the-fold size adjustment keep the view still); the content lanes cache every loaded preview/diff, and source revisions plus context sequences fence obsolete replies. A **Type** filter (all · pending · Git) narrows the layers; the repository picker uses the Git panel's `resolveRepoLabel`. **Approve N** counts records with no acknowledgement on their own fields (`ackHintFor`, the server preview's own predicate); the rest open `PendingReviewDialog` grouped by reason, each record sent with its own fingerprint, and every sweep reports a per-record result row (a refused revert included). The orphan probe runs on mount, Refresh and Repair only — a source revision re-reads the plain list. Source inventory is separate from content, completed siblings are cached, truncation is shown, and Git errors do not hide other sources. Review reads a bounded pending preview/fingerprint with typed current/baseline/index states; ack-required state opens an explicit confirmation. Actual WRITE access gates card, compact, bulk and repair controls; historical cards remain read-only. Orphan ids arrive with the same pending listing, so Repair remains orphan-only and re-authorized server-side.
30
+ - `PendingChangesPanel` — Changes groups current Git status and pending review by source/repository, including non-Git sources. Normalized URI joining renders a file once while retaining separately labeled staged/unstaged comparisons for each overlapping repository and one pending review. All cards start expanded in ONE `useVirtualizer` list (`pendingChangesModel.ts` owns the filters, the flat row list, `rowEstimate`/`isMeasuredRow` and the Approve split; the compact row pins `h-8`, every card is measured; `overflow-anchor:none` plus an above-the-fold size adjustment keep the view still); the content lanes cache every loaded preview/diff, and source revisions plus context sequences fence obsolete replies. A **Type** filter (all · pending · Git) narrows the layers; the repository picker uses the Git panel's `resolveRepoLabel`. **Approve N** counts records with no acknowledgement on their own fields (`ackHintFor`, the server preview's own predicate) or in an already-loaded preview (`splitApprovable`'s `knownPreview`, no fetch of its own); the rest open `PendingReviewDialog` grouped by reason, each record sent with its own fingerprint, and every sweep reports a per-record result row (a refused revert included) — a record the sweep found needing an acknowledgement is reported AND handed to that grouped dialog (approve or dismiss with the acknowledgement). A batch (the sweep, a dialog group) HOLDS the source revisions its own writes bump (`holdRevisions`, released in `afterMutation`): N writes cost N verb requests, ONE re-list and at most one re-preview per card still listed — never a re-list plus every visible card's re-preview per write. A `changed` answer (`PENDING_STATE_CHANGED` / `PENDING_GENERATION_CHANGED`) is not final: the dialog fetches ONE fresh preview for that record (per-record sequence, liveness cleared on unmount only) and waits for a new click — never a replay. In the panel that ONE fetch is the panel's `refetch`: it waits for the re-list, the record's card holds meanwhile and adopts the answer under its final key (`takeRefetch`), so the card sends nothing of its own; a direct (dialog-less) refusal bumps only that card's preview nonce. A confirmed absence with a saved baseline is named in the preview body (`acceptsAbsence`). The orphan probe runs on mount, Refresh and Repair only — a source revision re-reads the plain list. Source inventory is separate from content, completed siblings are cached, truncation is shown, and Git errors do not hide other sources. Review reads a bounded pending preview/fingerprint with typed current/baseline/index states; ack-required state opens an explicit confirmation. Actual WRITE access gates card, compact, bulk and repair controls; historical cards remain read-only. Orphan ids arrive with the same pending listing, so Repair remains orphan-only and re-authorized server-side.
31
31
 
32
32
  - `InlineUploader` — the Upload tab (folder pick, new folder, drop zone). The two modals it once sat beside (`EditFileModal`, `UploadFileModal`) were deleted 2026-09-04: nothing had mounted either since the repository's baseline, so the settling-sandpiper M9 reconcile tails had been fixed on dead code while the live in-place Edit still called the upsert-only `refresh`. Those tails are store actions now (`saveFileInPlace` / `markFileDeleted`, `../store/README.md`) that `FileEditor` calls.
33
33
  - `GitControlPanel`, `StackedSections` (+ pure `stackedSectionsLayout.ts`), `GitCommitGraph` (+ pure `gitGraphLayout.ts`/`gitGraphPaging.ts`/`gitGraphItems.ts` — the item model: expandable rows, the outgoing divider, ref-chip/dot classing, the branch-bar ahead/behind chips), `GitDiffView` + `GitFileDiff` + `GitCommitChangesView` (re-exported; + pure `gitDiffExpand.ts`/`gitDiffPatch.ts`/`gitFileDiffMode.ts`), `GitStatusBadge`, `DiffCountBadge` — the Git Control surface: the sidebar pane-stack panel, the in-panel virtualized commit graph with commit drill-in + outgoing markers, the side-by-side diff view (single + multi-file stacked tab), the per-file tree badge. The owning narrative (sections, paging/live-refresh, drill-in, outgoing, caps, write gating) is the package-root README "Git Control panel" section — this bullet only anchors the files.
@@ -38,19 +38,20 @@ Re-exported from `app/filesystem/ui/index.ts`:
38
38
  - `SourceTypeIcon` — icon resolver per source kind.
39
39
  - `PolicyEditor` — compact admin-style permissions editor for the source-config panel. Renders the persisted JSON tree (`default`, `byRole`, `byUser`, `byAgent`, `paths[]`) with tri-state R/W/X pill badges (allow / deny / inherit cycle), per-role/user/agent override rows (with `+ Add` input + trash), and path rules with clickable role chips + per-rule agent chips (`$self/agentId`, inline-add). `OVR` toggles the rule's `override` flag. Adding a rule **prepends** it (visible at the top of long policy sets; first-match-wins means the fresh rule also takes precedence). Two modes: `edit` (Save button visible when dirty) and `view` (read-only chat-config render). It is wired into `SourcesPanel`, laid out admin-style, and carries the By Role / By User / By Agent sections. Save threads through `handlePolicySave` → `updateSourceConfig` without disturbing the user's pending sync/credential edits.
40
40
  - `SourcesPanel` "Discoverable" section — lives INSIDE the Vector Health expandable, alongside a `mount.mts` explainer card. Suggestions arrive from `GET /sources/discoverable` and use the shared `dimmedSourceTone('unattached')` util. The icon name (`AppWindow`, `GitBranch`, `FolderTree`, …) resolves through the shared `resolveLucideIcon` helper (same `ICON_MAP` as the workspace dock-left). Click pre-fills the add-source form with `{ key, path, kind, hostRoot? }` from the suggestion, plus the suggestion's optional `suggestedAccess` (read/write/exec) which overrides the connector-kind default, and — for `seed:'discoverable'` package declarations (e.g. the admin app zone, suggestion id `discover:decl:app`) — the **declared `description`** prefills the add form's description field. The **Full Host** suggestion (native/non-Docker only — `/` is the readable host root; in Docker the host must first be bound via `pnpm neuralis:mount add /`) ships `suggestedAccess: { read:true, write:false, exec:false }` so the whole-host root attaches **read-only by default** — the owner widens it (or narrows the uri-policy) in the form before saving. A host source is readable from the container shell, but host-native binaries still won't execute under the container runtime (a host PTY broker is a separate, future capability).
41
- - `SourcesPanel` row actions say what they do: **Rescan changes** (the `reindex` route without `force` — changed files are embedded again, unchanged ones keep their vectors), **Re-embed…** (`ReembedDialog` — every file of the source with the active model, estimate first), **Forget sync state** (the `reset` route — the next sync compares every file again; deletes no file and no vector). A started re-embed is followed by the same job poll + count refresh as a rescan (`followReindex`).
41
+ - `SourcesPanel` row actions say what they do: **Rescan changes** (the `reindex` route without `force` — changed files are embedded again, unchanged ones keep their vectors), **Re-embed…** (`ReembedDialog` — every file of the source with the active model, estimate first), **Embed pending…** (`ReembedDialog kind="pending"`; it and **Re-embed…** are disabled while the row's live `embedding_enabled` says off), **Forget sync state** (the `reset` route — the next sync compares every file again; deletes no file and no vector). A started re-embed is followed by the same job poll + count refresh as a rescan (`followReindex`); an embedding pass that ends `paused` (terminal, not failed) shows "Embedding paused: <reason>" with **Resume** (the same owed-only apply). The Modify-source body states `binarySkipLine` ("N binary files not indexed" — the last count a pass measured) and `embeddingLine` beside `mediaSkipLine`: off — the live `embedding_enabled`, never the last pass — reads neutral ("Semantic embedding off — text and field search only"); the last pass's other reasons read amber ("Semantic embedding pending: <reasons>").
42
42
  - `SourcesPanel` row is **name-first** (the redesign for the always-narrow widget). The source name is the dominant element (`flex-1`, larger/bold); the kind icon uses the SAME resolved icon set as the file tree (`resolveIconWithFallback` over the connector-manifest `kind_icon`/`meta.icon`) and is **coloured by `getSourceVisualTone`** — so the icon tone IS the sync/live signal, matching the tree (the old `Healthy/Idle/Error` status text + `statusTone` are gone). The per-row **trust badge** (`first-party`/…) and the **scope badge** (`project`/`user`/`agent` — already a tab filter above) are removed; only `observed`, `orphan`, `unsaved`, and the enabled toggle remain inline. Rows are **grouped by `origin_package`** — the **declaring package id** the server resolves per source (the CONTRACT-BACKED `PackageSourceDeclaration` owner, returned on each `SourceRuntimeSummary` / `s.origin_package`), one group **per package**: each real `@neuralis/*` (or any other) package sorts **alphabetically** and user-added mounts collapse into a single **`Mounted (user-added)`** group that sorts **LAST** (`sourceOriginGroup` / `MOUNTED_GROUP`). The small left-indented group header **replaces** the per-row trust/`first-party` badge. The old client-side heuristic that inferred the owner from the connector kind's npm scope (`sourceOriginLabel`) is **gone** — attribution is now resolved server-side from the declarations (`matchDeclaredSource`, slug + resolved-root for rooted kinds; kind-only for rootless sandbox kinds), so a user mount named `app` that is NOT rooted at the platform app zone is NOT attributed to admin — the grouping is **unspoofable**.
43
43
  - `SourcesPanel` source body: when a row is expanded it renders at **full height** (no `max-h` clamp) so every Path Rule / uri-policy is visible and the outer panel scrolls — the closed state keeps the collapse animation. The Sync sub-section shows a per-trigger explainer (Manual = only the Sync button; Auto = file-watch + reconcile; Scheduled = interval) and clarifies the "Always active" pin. Disabling sync (unchecking "Enable sync" + Save) sends `clearSync: true` — an omitted `sync` means "no change" server-side, so without the flag the disable would silently revert.
44
44
  - `SourcesPanel` per-source counts are **lazy** — the exact `connector` / `indexed` / `unindexed` stats row is fetched ONLY when its row is opened (`toggleSource` → `fetchSourceCounts` → a list scoped to `<source>://` with `include_counts`, so `computeSourcesRegistry` walks that ONE source), cached in `countsBySource`, and shown behind a "Loading counts…" placeholder. Panel-open itself does a **count-FREE** `refresh()` (no `withCounts`) — that still populates `sourceSummaries` (`s.sources`) for source discovery (observed-only sources, project scope) + per-row connector liveness dim, but fires **zero** project-wide count walks. There is deliberately no eager `refresh({withCounts:true})` on open — that, together with the Vector Health walk below, is what used to make opening one panel slow everything. Counted refreshes are narrowed to the operations that actually MOVE a number: sync, reindex, and a config save that changed the sync FILTER set (`syncFilterChanged` — exclude/include/`maxScanFiles`/a cleared sync block, which `connector.count()` applies, so the on-disk total shifts at once). A permissions save, a delete and an add refresh count-FREE: the indexed count is a project-scoped `countByFilter` with no per-caller policy input, the on-disk count is physical truth, a deleted row is gone and a brand-new row's numbers are one expand away. Every counted path also evicts that source's cached counts (`clearCachedCounts`) so the fresh summary is what renders — the lazy cache used to shadow it indefinitely after an exclude edit. `pollSyncJob` (1.5 s × `MAX_POLLS` 2400 = a sixty-minute ceiling) checks a `mountedRef` each turn and returns the moment the panel unmounts; the ref is cleared on UNMOUNT ONLY, never in a dependency-change cleanup, so a live poll is never dropped while the panel is still there. And EVERY hook in `SourcesPanel` sits above its `if (!open) return null` guard: two `useCallback`s used to live below it, so the component rendered a different number of hooks open than closed — React refuses that on one instance, and only the parent's mount gate carrying the same `workspacePanels.sources` flag kept it from ever firing.
45
45
  - `useFilesystemSSE` `sync:completed`: the handler is matched **before** the `if (!uri) return` guard — the event carries `source` (the slug), NOT a file `uri`. It runs the authoritative `reconcileSourceFolders` over that source's VISIBLE folders (a sync can have removed files), skips the pass entirely when every change count on the payload is zero (a missing count is unknown and still re-lists), and falls back to `refreshTree` only when no source is present. That pass rides the ONE per-source reconcile schedule shared with external-change events (a trailing quiet window with a max-wait), so a burst of changes becomes one pass. Owner: `app/filesystem/hooks/README.md`.
46
46
  - `lib/iconResolver.ts` (2026-05-07) — single source of truth for lucide icon name → component lookup. Exports `ICON_MAP`, `resolveLucideIcon`, `resolveIconWithFallback`, and the `IconComponent` type. Both PascalCase keys (mirror lucide-react) and lowercase aliases (`agent`, `admin`, `brain`, `terminal`, …). The workspace dock-left re-exports it via `neuralis/src/workspace/shell/dockIcons.ts`; the filetree's `usePackageIconBySlug` hook (in `host/buildFilesystemHostValue.ts`) feeds it package-icon names from `/packages/overview` so `packages://<slug>/` and `data://<slug>/` top-level folders render the package's dock-icon instead of the generic Folder.
47
47
  - `VectorHealthPanel` per-source table — joins `getSourcesHealth()` with `checkVectorHealth()` to surface a real "Last sync" column (relative age + sync-status colour) per source, instead of being a counts-only view.
48
+ - `VectorHealthPanel` heads its stats with ONE `semanticLine` from the report's `embedding` block (`Semantic: off / metadata only / dense (N missing · M stale)`).
48
49
  - `VectorHealthPanel` = **janitor** — the panel's role is "remove leftover records" (deleted past retention, orphaned chunks, duplicate report). Full per-source vector wipe lives on `SourceDeleteConfirm`, not here. `dryRun` default is `false` (clicking actually deletes); destructive buttons need a 2-click confirm; the Deleted stat card shows a `before → after (−Δ)` line after `GC`. Per-action explainer copy + tooltips spell out what each button does (GC / Repair are destructive; Deduplicate is read-only on the service side).
49
- - `VectorHealthPanel` is **lazy** — `SourcesPanel` renders it ONLY when the "Vector Health" section is expanded (`{healthOpen && …}`), never a CSS-only collapse, because the panel auto-fetches on mount; a mounted-but-hidden panel would fire the health request every time the Sources tab opens. On open it fetches the CHEAP count stats (`checkVectorHealth` → `/health`, count-only). The **Orphans** card and **Duplicate groups** count come from the EXPENSIVE on-demand scan (`scanVectorHealth` → `/health/scan`) behind an explicit **"Scan for problems"** button — they show `—` until the user runs it (and re-scan after a destructive action only if a scan was already loaded). This is the "ne lassítson 1 dolog mindent" rule: opening Sources triggers no collection walk.
50
+ - `VectorHealthPanel` is **lazy** — `SourcesPanel` renders it ONLY when the "Vector Health" section is expanded (`{healthOpen && …}`, the store's `vectorHealthOpen`, so a notification's `open-vector-health` nav can open it; a `contextResetPatch` key), never a CSS-only collapse, because the panel auto-fetches on mount; a mounted-but-hidden panel would fire the health request every time the Sources tab opens. On open it fetches the CHEAP count stats (`checkVectorHealth` → `/health`, count-only). The **Orphans** card and **Duplicate groups** count come from the EXPENSIVE on-demand scan (`scanVectorHealth` → `/health/scan`) behind an explicit **"Scan for problems"** button — they show `—` until the user runs it (and re-scan after a destructive action only if a scan was already loaded). This is the "ne lassítson 1 dolog mindent" rule: opening Sources triggers no collection walk.
50
51
  - `SourcesPanel` add form, the ROOT field — **derived from the declaration, not from `isDocker` and not from the kind name** (`lib/sourceKindForm.ts` `rootFieldMeta`, drift-tested against the real manifests in `test/ui/sourceKindForm.test.ts`). Every other field in that form already rendered from its declared `x-neuralis.ui`; `root` was the one hand-written, kind-BLIND exception, which is why the **Host Machine** kind used to show the `local` connector's `/mounts` volume picker, its `pnpm neuralis:mount add` callout and a `/mounts/...` placeholder while its own declaration said `Host root path` / `/home/you/projects` (warded-wren, closing the UI half of F-B5). The discriminator is the declared `x-neuralis.ui.control` on `root`: `container-path` gets the volume picker + callout + Browse; `host-path` gets none of them; a bare legacy `path` (or an absent control) is treated as container-plane, so an externally authored connector is unchanged. **Not `isHostPlaneKind`** — that is a NAME test whose own docstring says a future `ssh`/`smb` kind joins the plane by being added to its body, so a negative test on it would hand the container chrome to every future non-`local` kind. Browse is additionally gated on `canMount` (the shared `meetsRequires` mirror `FileTree` uses) and stays absent for host-plane roots at all times: `GET /sources/browse` readdirs in the SERVER process, the wrong machine for a broker-resolved host root — a host-plane root browser would need a broker-backed listing route, which does not exist.
51
52
  - `SourcesPanel` description field — the VALUE is empty whenever the description is RESOLVED rather than stored (`description_effective`), and that is load-bearing: prefilling the value would freeze a copy of the package's wording into this source's config and stop it tracking the package. The resolved text therefore rides as the **placeholder** in edit state (with a one-line note saying an empty box keeps following the package) and as **labelled** text — `"… (from the package)"` — in the read state and the collapsed summary. **Both states must carry the label**: live-testing warded-wren caught it present in the read state and absent in the edit state, where the box simply looked empty; an opacity difference alone is not a label, because it does not survive the state that matters (editing).
52
53
 
53
- - `SourceDeleteConfirm` — inline rose-tinted popover under the row (no modal, matches the panel's "no overlay" pattern). Headline + one-line reminder that the host folder is not touched + `[ ] Purge vector index entries (~N point(s))` checkbox (default OFF — conservative) + Docker-bind-mount warning ribbon when `connection.config.hostRoot` is set, telling the user to run `pnpm neuralis:mount remove <slug>` before restarting the container. On confirm, `SourcesPanel.handleDelete` calls `deleteSource({ purgeVectors })` and surfaces a transient `successMsg` ("Deleted "<source>" (vectors purged: N)").
54
+ - `SourceDeleteConfirm` — inline rose-tinted popover under the row (no modal, matches the panel's "no overlay" pattern). Headline + one-line reminder that the host folder is not touched + `[ ] Purge vector index entries (~N point(s))` checkbox (default OFF — conservative) + Docker-bind-mount warning ribbon when `connection.config.hostRoot` is set, telling the user to run `neuralis mount remove <slug>` before restarting the container. On confirm, `SourcesPanel.handleDelete` calls `deleteSource({ purgeVectors })` and surfaces a transient `successMsg` ("Deleted "<source>" (vectors purged: N)").
54
55
  - **`SkillLauncher` coverage** (`@neuralis/package-system/client`, additive "+" launchers) — every filesystem function the LLM can drive has a visible composer-prefill trace. Clicking a launcher PREFILLS the chat composer with the skill (+ scripts) like a `/` slash pick; it never sends/streams. **No launcher states a feature list**: visibility is DERIVED from the host's per-caller skill catalog, so it cannot drift from each skill's own `SKILL.md` `requiredFeatures` and an unresolved catalog renders nothing (the gate named per row below is the SKILL's, documented here, not repeated in the JSX). `packageId` is the REGISTRY id (`@neuralis/brain-core`), matched as an exact pair with the skill id. The launchers resolve the `WorkspaceHostPort` from the `WorkspaceHostPortProvider` mounted once in `host/installFilesystemHostComponents.tsx` — without it they render `null` silently. Current placements:
55
56
  - `Toolbar` — `sync` (`drive.sync`, icon-`brain`, script-less) next to the Sources panel toggle, and `manage-files` (`drive.write`; `scripts: mkdir, copy, modify, search`) as the general file-ops handoff. The tree's CONTEXT MENU deliberately carries none: it is a keyboard-navigable click-to-act list with one uniform item model, and a composer-prefill affordance would need a bespoke item kind there.
56
57
  - `SourcesPanel` header — `manage-sources` (`drive.read`, attached-left to the `+ Source` button; `scripts: sync, reindex, set-enabled`).
@@ -69,7 +70,7 @@ Re-exported from `app/filesystem/ui/index.ts`:
69
70
 
70
71
  ### `recognizesPackages` source toggle + package access-feature governance
71
72
 
72
- - **`SourcesPanel`** renders a **Recognize packages** checkbox on BOTH the Add-source wizard and the Modify-source body — shown only for `local`-kind sources AND only to an owner/admin-effective caller (`host.currentRole.priority <= 2`); it is hidden otherwise and the server re-gates. On create it rides the `createSource` body; on edit it joins the shared draft + Save (and is **stripped** from the PATCH of any caller below that strength, so an unrelated description/sync edit never trips the server's strength gate). Turning it on makes the source a runtime-WASM package source (additive to `_packages/`); the loaded packages are advertised only within the source's own scope.
73
+ - **`SourcesPanel`** renders a **Recognize packages** checkbox on BOTH the Add-source wizard and the Modify-source body — shown only for `local`-kind sources AND only to an owner/admin-effective caller (`host.currentRole.priority <= 2`); it is hidden otherwise and the server re-gates. On create it rides the `createSource` body; on edit it joins the shared draft + Save (and is **stripped** from the PATCH of any caller below that strength, so an unrelated description/sync edit never trips the server's strength gate). Turning it on makes the source a runtime package source (additive to `_packages/`); the loaded packages are advertised only within the source's own scope.
73
74
  - **`PackagesPanel`** is the consumption-governance surface. Per package (first-party AND project WASM — both appear in the snapshot it lists), an owner/admin-effective caller sees a **base-access feature** control: attach or clear an override feature id (`ProjectRecord.packageAccessFeature`, restrict-only) that hides the whole package from anyone lacking it. The control is a **datalist picker** sourced from the project feature catalog (`getGrantableFeatureIds` → `/api/admin/feature-catalog`; free-text fallback if the fetch fails) — datalist, not a native `<select>`, to avoid the option-popup opacity gotcha; the route rejects an unknown feature id (400). A manifest-declared `requires.accessFeature` shows read-only (cannot be cleared here; an override adds an additional requirement). Non-owners see the effective feature read-only. (Grant the chosen feature to a role in Admin → Roles — a declared/overridden access feature is grantable there.)
74
75
 
75
76
  ## Security & scope
@@ -17,7 +17,8 @@
17
17
  import { useEffect, useState } from 'react';
18
18
  import { createPortal } from 'react-dom';
19
19
  import { Loader2, RefreshCcw, X } from 'lucide-react';
20
- import { estimateReembed, startReembed, type ReembedEstimate } from '../api';
20
+ import { estimateReembed, startReembed, type ReembedEstimate, type ReembedKind } from '../api';
21
+ import { useFilesystemStore } from '../store/filesystemStore';
21
22
 
22
23
  export type ReembedTarget = {
23
24
  source: string;
@@ -47,14 +48,18 @@ export function ReembedDialog({
47
48
  target,
48
49
  onClose,
49
50
  onStarted,
51
+ kind = 'reembed',
50
52
  }: {
51
53
  agentId: string;
52
54
  projectId?: string;
53
55
  target: ReembedTarget;
56
+ /** `pending` embeds only what is owed (missing / stale) — the same estimate-first confirm. */
57
+ kind?: ReembedKind;
54
58
  onClose: () => void;
55
59
  /** Called after a job was queued — the caller follows it however it follows sync jobs. */
56
60
  onStarted?: () => void;
57
61
  }) {
62
+ const client = useFilesystemStore((s) => s.client);
58
63
  const [estimate, setEstimate] = useState<ReembedEstimate | null>(null);
59
64
  const [estimating, setEstimating] = useState(true);
60
65
  const [starting, setStarting] = useState(false);
@@ -72,7 +77,7 @@ export function ReembedDialog({
72
77
  useEffect(() => {
73
78
  let live = true;
74
79
  setEstimating(true);
75
- estimateReembed(scope)
80
+ estimateReembed(scope, kind, client)
76
81
  .then((result) => {
77
82
  if (live) setEstimate(result);
78
83
  })
@@ -105,7 +110,7 @@ export function ReembedDialog({
105
110
  setStarting(true);
106
111
  setError(null);
107
112
  try {
108
- const started = await startReembed(scope);
113
+ const started = await startReembed(scope, kind, client);
109
114
  if (!started.job) {
110
115
  // Nothing readable in scope — the route queued nothing. Say so.
111
116
  setNothingToDo(true);
@@ -129,17 +134,25 @@ export function ReembedDialog({
129
134
  >
130
135
  <div
131
136
  role="dialog"
132
- aria-label={`Re-embed ${target.label}`}
137
+ aria-label={`${kind === 'pending' ? 'Embed pending files of' : 'Re-embed'} ${target.label}`}
133
138
  className="w-[360px] rounded-xl border border-cyan-400/25 bg-[#15151d] p-4 shadow-[0_30px_80px_-24px_rgba(0,0,0,0.85)]"
134
139
  >
135
140
  <div className="flex items-start gap-2.5">
136
141
  <RefreshCcw className="h-4 w-4 text-cyan-300 shrink-0 mt-0.5" />
137
142
  <div className="min-w-0 flex-1">
138
- <div className="truncate text-[12px] font-semibold text-white/90">Re-embed {target.label}?</div>
143
+ <div className="truncate text-[12px] font-semibold text-white/90">
144
+ {kind === 'pending' ? `Embed pending files of ${target.label}?` : `Re-embed ${target.label}?`}
145
+ </div>
139
146
  <div className="mt-1 text-[10.5px] text-white/55 leading-relaxed">
140
- Every file in this scope is read again and embedded with the <span className="text-white/75">active</span>{' '}
141
- model, whether it changed or not. File contents and history are not touched — only the vectors are
142
- replaced.
147
+ {kind === 'pending' ? (
148
+ <>Only files whose semantic embedding is missing or stale are embedded, with the{' '}
149
+ <span className="text-white/75">active</span> model. The figures below cover the whole scope — an upper
150
+ bound; files already embedded are skipped. Contents and text search are not touched.</>
151
+ ) : (
152
+ <>Every file in this scope is read again and embedded with the <span className="text-white/75">active</span>{' '}
153
+ model, whether it changed or not. File contents and history are not touched — only the vectors are
154
+ replaced.</>
155
+ )}
143
156
  </div>
144
157
  <div className="mt-2 rounded-md bg-white/[0.03] px-2 py-1.5 text-[10.5px] text-white/65 space-y-0.5">
145
158
  {estimating ? (
@@ -192,7 +205,7 @@ export function ReembedDialog({
192
205
  className="h-7 px-3 rounded-md text-[11px] font-semibold transition disabled:opacity-40 inline-flex items-center gap-1.5 bg-cyan-500/20 hover:bg-cyan-500/30 border border-cyan-400/40 text-cyan-100"
193
206
  >
194
207
  {starting ? <Loader2 className="h-3 w-3 animate-spin" /> : <RefreshCcw className="h-3 w-3" />}
195
- Re-embed
208
+ {kind === 'pending' ? 'Embed pending' : 'Re-embed'}
196
209
  </button>
197
210
  </div>
198
211
  </div>
@@ -75,7 +75,7 @@ export function SourceDeleteConfirm({
75
75
  if checked). To also drop the underlying compose bind, run the
76
76
  infra-only:{' '}
77
77
  <code className="font-mono not-italic text-amber-100/90">
78
- pnpm neuralis:mount remove {source}
78
+ neuralis mount remove {source}
79
79
  </code>
80
80
  </div>
81
81
  )}