@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
@@ -431,6 +431,7 @@ export function GitCommitGraph({
431
431
  * E) — two-part `slug:// Name` header render; absent → slug-only fallback. */
432
432
  repoName?: string;
433
433
  }) {
434
+ const client = useFilesystemStore((s) => s.client);
434
435
  const [page, setPage] = useState<GraphPageState>(emptyGraphPageState);
435
436
  const [loading, setLoading] = useState(false);
436
437
  const [error, setError] = useState<string | null>(null);
@@ -508,7 +509,7 @@ export function GitCommitGraph({
508
509
  ...(repo ? { repo } : {}),
509
510
  maxCount,
510
511
  skip,
511
- });
512
+ }, client);
512
513
  if (mySeq !== seqRef.current) return;
513
514
  const fetched = {
514
515
  commits: Array.isArray(res?.commits) ? res.commits : [],
@@ -530,7 +531,7 @@ export function GitCommitGraph({
530
531
  ...(repo ? { repo } : {}),
531
532
  maxCount,
532
533
  skip,
533
- })
534
+ }, client)
534
535
  .then((stats) => {
535
536
  if (mySeq !== seqRef.current) return;
536
537
  setCommitStats((cur) => (mode === 'append' ? { ...cur, ...stats.stats } : { ...stats.stats }));
@@ -643,7 +644,7 @@ export function GitCommitGraph({
643
644
  source,
644
645
  ...(repo ? { repo } : {}),
645
646
  sha,
646
- });
647
+ }, client);
647
648
  if (epoch !== filesEpochRef.current) return;
648
649
  setCommitFiles((prev) =>
649
650
  new Map(prev).set(
@@ -702,7 +703,7 @@ export function GitCommitGraph({
702
703
  ...(projectId ? { projectId } : {}),
703
704
  source,
704
705
  ...(repo ? { repo } : {}),
705
- });
706
+ }, client);
706
707
  if (epoch !== outgoingEpochRef.current) return;
707
708
  // 3b: a deep-fail is a typed `{ok:false,'unavailable'}` refusal now — treat
708
709
  // it like no-upstream (nothing to show), never crash on a missing files[].
@@ -812,7 +813,7 @@ export function GitCommitGraph({
812
813
  ...(projectId ? { projectId } : {}),
813
814
  source,
814
815
  ...(repo ? { repo } : {}),
815
- });
816
+ }, client);
816
817
  if (epoch !== outgoingEpochRef.current) return;
817
818
  // 3b: a deep-fail is a typed `{ok:false,'unavailable'}` refusal now — treat
818
819
  // it like no-upstream (nothing to show), never crash on a missing files[].
@@ -453,6 +453,7 @@ type ConfirmState = { title: string; message: string; confirmLabel: string; onCo
453
453
  type RescueState = { message: string; rescue: GitRescue };
454
454
 
455
455
  export function GitControlPanel({ agentId }: { agentId: string }) {
456
+ const client = useFilesystemStore((s) => s.client);
456
457
  const sources = useFilesystemStore((s) => s.sources);
457
458
  const projectId = useFilesystemStore((s) => s.activeProjectId);
458
459
  const activeSource = useFilesystemStore((s) => s.activeSource);
@@ -554,7 +555,7 @@ export function GitControlPanel({ agentId }: { agentId: string }) {
554
555
  if (cached && (cached instanceof Promise || !opts?.force)) return cached;
555
556
  const task = (async () => {
556
557
  try {
557
- const res = await getGitRepos({ agentId, ...(projectId ? { projectId } : {}), source: slug });
558
+ const res = await getGitRepos({ agentId, ...(projectId ? { projectId } : {}), source: slug }, client);
558
559
  const repos = Array.isArray(res?.repos) ? res.repos : [];
559
560
  repoListsRef.current.set(slug, repos);
560
561
  setRepoLists((prev) => new Map(prev).set(slug, repos));
@@ -799,7 +800,7 @@ export function GitControlPanel({ agentId }: { agentId: string }) {
799
800
  const handleStage = useCallback(
800
801
  (paths: string[]) => {
801
802
  if (paths.length === 0) return;
802
- void doWrite(() => stagePaths({ agentId, ...p, source: selectedSlug, paths }));
803
+ void doWrite(() => stagePaths({ agentId, ...p, source: selectedSlug, paths }, client));
803
804
  },
804
805
  [doWrite, agentId, p, selectedSlug],
805
806
  );
@@ -807,7 +808,7 @@ export function GitControlPanel({ agentId }: { agentId: string }) {
807
808
  const handleUnstage = useCallback(
808
809
  (paths: string[]) => {
809
810
  if (paths.length === 0) return;
810
- void doWrite(() => unstagePaths({ agentId, ...p, source: selectedSlug, paths }));
811
+ void doWrite(() => unstagePaths({ agentId, ...p, source: selectedSlug, paths }, client));
811
812
  },
812
813
  [doWrite, agentId, p, selectedSlug],
813
814
  );
@@ -815,7 +816,7 @@ export function GitControlPanel({ agentId }: { agentId: string }) {
815
816
  const runDiscard = useCallback(
816
817
  (paths: string[]) => {
817
818
  void (async () => {
818
- const res = await doWrite(() => discardPaths({ agentId, ...p, source: selectedSlug, paths }));
819
+ const res = await doWrite(() => discardPaths({ agentId, ...p, source: selectedSlug, paths }, client));
819
820
  if (res && res.rescue) {
820
821
  setRescue({
821
822
  message: `Discarded ${paths.length} file${paths.length === 1 ? '' : 's'} — recoverable`,
@@ -875,7 +876,7 @@ export function GitControlPanel({ agentId }: { agentId: string }) {
875
876
  const message = commitMessage.trim();
876
877
  if (!message) return;
877
878
  void (async () => {
878
- const res = await doWrite(() => commitGit({ agentId, ...p, source: selectedSlug, message, amend }));
879
+ const res = await doWrite(() => commitGit({ agentId, ...p, source: selectedSlug, message, amend }, client));
879
880
  if (res && res.ok) {
880
881
  setCommitMessage('');
881
882
  setAmend(false);
@@ -901,7 +902,7 @@ export function GitControlPanel({ agentId }: { agentId: string }) {
901
902
  (ref: string) => {
902
903
  setBranchPickerOpen(false);
903
904
  void (async () => {
904
- const res = await doWrite(() => checkoutGitBranch({ agentId, ...p, source: selectedSlug, ref }));
905
+ const res = await doWrite(() => checkoutGitBranch({ agentId, ...p, source: selectedSlug, ref }, client));
905
906
  if (res)
906
907
  void loadGitBranches({ agentId, projectId: projectId ?? null, source: selectedSlug, repo: activeRepoRel });
907
908
  })();
@@ -920,10 +921,10 @@ export function GitControlPanel({ agentId }: { agentId: string }) {
920
921
  void (async () => {
921
922
  // Create then switch to it (VS-Code "Create new branch" also checks out).
922
923
  const created = await doWrite(() =>
923
- createGitBranch({ agentId, ...p, source: selectedSlug, name, ...(from ? { startPoint: from } : {}) }),
924
+ createGitBranch({ agentId, ...p, source: selectedSlug, name, ...(from ? { startPoint: from } : {}) }, client),
924
925
  );
925
926
  if (created) {
926
- await doWrite(() => checkoutGitBranch({ agentId, ...p, source: selectedSlug, ref: name }));
927
+ await doWrite(() => checkoutGitBranch({ agentId, ...p, source: selectedSlug, ref: name }, client));
927
928
  void loadGitBranches({ agentId, projectId: projectId ?? null, source: selectedSlug, repo: activeRepoRel });
928
929
  }
929
930
  })();
@@ -932,7 +933,7 @@ export function GitControlPanel({ agentId }: { agentId: string }) {
932
933
  const runDeleteBranch = useCallback(
933
934
  (name: string) => {
934
935
  void (async () => {
935
- const res = await doWrite(() => deleteGitBranch({ agentId, ...p, source: selectedSlug, name }));
936
+ const res = await doWrite(() => deleteGitBranch({ agentId, ...p, source: selectedSlug, name }, client));
936
937
  if (res && res.rescue) {
937
938
  setRescue({ message: `Deleted branch "${name}" — recoverable`, rescue: res.rescue });
938
939
  }
@@ -966,7 +967,7 @@ export function GitControlPanel({ agentId }: { agentId: string }) {
966
967
  if (!rescue) return;
967
968
  const carrier = rescue.rescue;
968
969
  setRescue(null);
969
- void doWrite(() => restoreGit({ agentId, ...p, source: selectedSlug, rescue: carrier }));
970
+ void doWrite(() => restoreGit({ agentId, ...p, source: selectedSlug, rescue: carrier }, client));
970
971
  }, [rescue, doWrite, agentId, p, selectedSlug]);
971
972
 
972
973
  // --- Remote operations (G4 keyring-kestrel Slice 2) ------------------------
@@ -976,17 +977,17 @@ export function GitControlPanel({ agentId }: { agentId: string }) {
976
977
 
977
978
  const handleFetch = useCallback(() => {
978
979
  setRemotePickerOpen(false);
979
- void doWrite(() => fetchGit({ agentId, ...p, source: selectedSlug }));
980
+ void doWrite(() => fetchGit({ agentId, ...p, source: selectedSlug }, client));
980
981
  }, [doWrite, agentId, p, selectedSlug]);
981
982
 
982
983
  const handlePull = useCallback(() => {
983
984
  setRemotePickerOpen(false);
984
- void doWrite(() => pullGit({ agentId, ...p, source: selectedSlug }));
985
+ void doWrite(() => pullGit({ agentId, ...p, source: selectedSlug }, client));
985
986
  }, [doWrite, agentId, p, selectedSlug]);
986
987
 
987
988
  const handlePush = useCallback(() => {
988
989
  setRemotePickerOpen(false);
989
- void doWrite(() => pushGit({ agentId, ...p, source: selectedSlug }));
990
+ void doWrite(() => pushGit({ agentId, ...p, source: selectedSlug }, client));
990
991
  }, [doWrite, agentId, p, selectedSlug]);
991
992
 
992
993
  const unstagedChanges = gitStatus?.unstaged ?? [];
@@ -26,11 +26,12 @@ export function GitDiffView({
26
26
  agentId: string | null;
27
27
  projectId: string | null;
28
28
  }): React.ReactElement {
29
+ const client = useFilesystemStore((s) => s.client);
29
30
  const [canWrite, setCanWrite] = useState(false);
30
31
  const revision = useFilesystemStore(state => state.sourceChangeRevision[target.source] ?? 0);
31
32
  useEffect(() => {
32
33
  let alive = true; setCanWrite(false);
33
- if (agentId) void getGitRepos({ agentId, ...(projectId ? { projectId } : {}), source: target.source }).then(result => {
34
+ if (agentId) void getGitRepos({ agentId, ...(projectId ? { projectId } : {}), source: target.source }, client).then(result => {
34
35
  if (alive) setCanWrite(result.repos.find(repo => repo.rel === (target.repo ?? ''))?.canWrite === true);
35
36
  }).catch(() => { if (alive) setCanWrite(false); });
36
37
  return () => { alive = false; };
@@ -36,7 +36,7 @@ import {
36
36
  import { computeGitDiffTokens, renderShikiToken } from './gitDiffShikiTokenizer';
37
37
  import { resolveShikiLang, isShikiReady, ensureLanguage, getHighlighter } from './codeEditor/shikiHighlighter';
38
38
  import { detectCodeLanguage } from '../lib/detectLanguage';
39
- import { useContainerWidth } from '../hooks/useContainerWidth';
39
+ import { useContainerWidth } from '@neuralis/package-system/client';
40
40
  import type { VisibleDiffQueue } from '../hooks/useVisibleDiffCards';
41
41
  import { DiffModeToggle } from './DiffModeToggle';
42
42
 
@@ -156,6 +156,7 @@ export function GitFileDiff({
156
156
  /** The lane's synchronous cache read: a re-mounted card paints its last answer on its first frame. */
157
157
  peekContent?: VisibleDiffQueue['peek'];
158
158
  }): React.ReactElement {
159
+ const client = useFilesystemStore((s) => s.client);
159
160
  const [initial] = useState(() => peekContent?.<DiffAnswer>(`${diffFamily(source, repo, path, mode)}\u0000${revision}`, diffFamily(source, repo, path, mode)));
160
161
  const [raw, setRaw] = useState<string | null>(() => initial === undefined ? null : initial && initial.ok !== false ? initial.diff : '');
161
162
  const [loading, setLoading] = useState(false);
@@ -262,7 +263,7 @@ export function GitFileDiff({
262
263
  path,
263
264
  // ONE mode per request — the server 400s any combination.
264
265
  ...diffRequestParams(normalizedMode),
265
- });
266
+ }, client);
266
267
  // Keyed on everything the answer depends on; after a hunk apply the refetch
267
268
  // replaces the cached answer instead of reading it.
268
269
  const family = diffFamily(source, repo, path, normalizedMode);
@@ -364,7 +365,7 @@ export function GitFileDiff({
364
365
  ...(repo ? { repo } : {}),
365
366
  path,
366
367
  rev: expanderSrc.rev,
367
- });
368
+ }, client);
368
369
  const res = runContent ? await runContent(fetchSource, () => mySeq === seqRef.current) : await fetchSource();
369
370
  if (!res || mySeq !== seqRef.current) return null;
370
371
  return settle(res.ok ? { ok: true, content: res.content } : { ok: false });
@@ -376,7 +377,7 @@ export function GitFileDiff({
376
377
  agentId,
377
378
  ...(projectId ? { projectId } : {}),
378
379
  files: [`${source}://${repo ? `${repo}/` : ''}${path}`],
379
- });
380
+ }, client);
380
381
  const res = runContent ? await runContent(fetchSource, () => mySeq === seqRef.current) : await fetchSource();
381
382
  if (!res || mySeq !== seqRef.current) return null;
382
383
  const file = extractReadFilesPayload(res)[0];
@@ -467,9 +468,9 @@ export function GitFileDiff({
467
468
  setHunkError(null);
468
469
  try {
469
470
  if (staged) {
470
- await unstageHunk({ agentId, ...(projectId ? { projectId } : {}), source, ...(repo ? { repo } : {}), hunk: patch });
471
+ await unstageHunk({ agentId, ...(projectId ? { projectId } : {}), source, ...(repo ? { repo } : {}), hunk: patch }, client);
471
472
  } else {
472
- await stageHunk({ agentId, ...(projectId ? { projectId } : {}), source, ...(repo ? { repo } : {}), hunk: patch });
473
+ await stageHunk({ agentId, ...(projectId ? { projectId } : {}), source, ...(repo ? { repo } : {}), hunk: patch }, client);
473
474
  }
474
475
  // The index changed → the cached `rev=index` source is stale (the WT
475
476
  // entry would survive, but a whole-map clear is cheap and race-free).
@@ -23,7 +23,7 @@ import {
23
23
  ChevronLeft, CheckCheck, X, XCircle, Wrench, Unlink, ShieldAlert,
24
24
  } from 'lucide-react';
25
25
  import { SkillLauncher } from '@neuralis/package-system/client';
26
- import { useFilesystemStore } from '../store/filesystemStore';
26
+ import { useFilesystemStore, useFilesystemStoreApi } from '../store/filesystemStore';
27
27
  import {
28
28
  listPendingChanges, previewPendingChange, discardPendingChange, discardOrphanedPending,
29
29
  type PendingChangeRecord, type PendingPreview,
@@ -98,18 +98,24 @@ export function CurrentGitDiffs({ uri, rows: gitRows, agentId, projectId, canWri
98
98
  </div>)}</React.Fragment>)}</>;
99
99
  }
100
100
 
101
+ /** The lane key one card's answer belongs to: the record and its source revision. */
102
+ const previewKey = (rec: PendingChangeRecord, revision: number): string => `preview\u0000${rec.id}\u0000${rec.updatedAt}\u0000${revision}`;
103
+
101
104
  const previewBytes = (p: PendingPreview): number =>
102
105
  2 * ((p.baseline.state === 'text' ? p.baseline.text.length : 0) + (p.current.state === 'text' ? p.current.text.length : 0)) + 256;
103
106
 
104
107
 
105
108
  export const PendingDiffCard = memo(function PendingDiffCard({
106
- rec, agentId, projectId, focused, orphaned, onOpen, onApprove, onRevert, onDiscard, onDismiss, onFocus, busy, queue, revision, canWrite, onPreview, peekPreview, extra,
109
+ rec, agentId, projectId, focused, orphaned, onOpen, onApprove, onRevert, onDiscard, onDismiss, onFocus, busy, queue, revision, canWrite, onPreview, peekPreview, takeRefetch, extra,
107
110
  }: {
108
111
  rec: PendingChangeRecord; agentId: string; projectId: string | null; focused: boolean; orphaned: boolean;
109
112
  onOpen: (uri: string) => void; onApprove: () => void; onRevert: () => void; onDiscard: () => void; onDismiss: () => void;
110
113
  onFocus: () => void; busy: boolean; queue: VisibleDiffQueue; revision: number; canWrite: boolean;
111
- onPreview: (preview: PendingPreview) => void; peekPreview: (id: string) => PendingPreview | undefined; extra?: React.ReactNode;
114
+ onPreview: (preview: PendingPreview) => void; peekPreview: (id: string) => PendingPreview | undefined;
115
+ /** The dialog's post-409 refetch: `'wait'` while in flight, then its answer ONCE for the key it was taken for. */
116
+ takeRefetch: (id: string, key: string) => PendingPreview | 'wait' | undefined; extra?: React.ReactNode;
112
117
  }) {
118
+ const client = useFilesystemStore((s) => s.client);
113
119
  // Cards open inline by default — the diff is the review unit.
114
120
  const [expanded, setExpanded] = useState(true);
115
121
  // The last answer this record had (a remount or a new source revision) stays on screen
@@ -126,13 +132,16 @@ export const PendingDiffCard = memo(function PendingDiffCard({
126
132
  active.current = expanded && shown;
127
133
  // The record + source revision this card's answer belongs to; leaving the viewport
128
134
  // keeps it (the lane cache answers a re-entry).
129
- const cacheKey = `preview\u0000${rec.id}\u0000${rec.updatedAt}\u0000${revision}`;
135
+ const cacheKey = previewKey(rec, revision);
130
136
  const loadedKey = useRef('');
131
137
  useEffect(() => {
132
138
  const generation = ++seq.current;
133
139
  if (!expanded || !shown || loadedKey.current === cacheKey) { setLoading(false); return; }
140
+ const adopted = takeRefetch(rec.id, cacheKey);
141
+ if (adopted === 'wait') { setLoading(true); return; }
142
+ if (adopted) { setPreview(adopted); setLoadError(null); setAnswerKey(cacheKey); loadedKey.current = cacheKey; setLoading(false); return; }
134
143
  setLoading(true);
135
- void queue.run(() => previewPendingChange({ agentId, ...(projectId ? { projectId } : {}), id: rec.id }),
144
+ void queue.run(() => previewPendingChange({ agentId, ...(projectId ? { projectId } : {}), id: rec.id }, client),
136
145
  () => generation === seq.current && active.current, { key: cacheKey, bytes: previewBytes }).then(next => {
137
146
  if (!next || generation !== seq.current || !active.current) return;
138
147
  onPreview(next);
@@ -204,10 +213,19 @@ const NO_FILTERS: ChangeFilters = { kind: 'all', source: '', repo: '', branch: '
204
213
  /** One bulk pass (the sweep and the grouped dialog) covers at most this many records. */ const REVIEW_BATCH = 100;
205
214
 
206
215
  export function PendingChangesPanel({ agentId, projectId }: Props) {
216
+ const client = useFilesystemStore((s) => s.client);
207
217
  const host = useFilesystemHost();
208
218
  const canWrite = hasFeature({ grantedFeatures: [...(host.grantedFeatures ?? [])] }, 'drive.write');
219
+ const storeApi = useFilesystemStoreApi();
209
220
  const sources = useFilesystemStore(s => s.sources);
210
- const revisions = useFilesystemStore(s => s.sourceChangeRevision);
221
+ const liveRevisions = useFilesystemStore(s => s.sourceChangeRevision);
222
+ // A review batch (the sweep, a dialog group) HOLDS the revisions its own writes bump: one re-list
223
+ // at its end (`afterMutation`) instead of a re-list and every visible card's re-preview per write.
224
+ const [held, setHeld] = useState<typeof liveRevisions | null>(null);
225
+ const revisions = held ?? liveRevisions;
226
+ const liveRef = useRef(liveRevisions); liveRef.current = liveRevisions;
227
+ const listedRevisions = useRef(liveRevisions);
228
+ const holdRevisions = useCallback(() => setHeld(prev => prev ?? liveRef.current), []);
211
229
  const pendingPanelFilter = useFilesystemStore((s) => s.pendingPanelFilter);
212
230
  const setPendingPanelFilter = useFilesystemStore((s) => s.setPendingPanelFilter);
213
231
  const openFile = useFilesystemStore((s) => s.openFile);
@@ -220,6 +238,16 @@ export function PendingChangesPanel({ agentId, projectId }: Props) {
220
238
  const queue = useVisibleDiffCards(scrollRoot);
221
239
  const previews = useRef(new Map<string, PendingPreview>());
222
240
  const peekPreview = useCallback((id: string) => previews.current.get(id), []);
241
+ // ONE fetch per dialog 409: the dialog's refetch waits for the re-list; the record's card
242
+ // holds (`null`) until it lands, then adopts it under the key the final nonce gives it.
243
+ const refetches = useRef(new Map<string, { key: string; preview: PendingPreview } | null>());
244
+ const relisted = useRef(Promise.withResolvers<PendingChangeRecord[] | undefined>());
245
+ const takeRefetch = useCallback((id: string, key: string): PendingPreview | 'wait' | undefined => {
246
+ const entry = refetches.current.get(id);
247
+ if (entry === null) return 'wait';
248
+ if (entry) refetches.current.delete(id);
249
+ return entry?.key === key ? entry.preview : undefined;
250
+ }, []);
223
251
  const [filters, setFilters] = useState<ChangeFilters>(NO_FILTERS);
224
252
  const setFilter = (key: keyof ChangeFilters) => (event: React.ChangeEvent<HTMLSelectElement>) =>
225
253
  setFilters(f => ({ ...f, [key]: event.target.value }));
@@ -229,7 +257,21 @@ export function PendingChangesPanel({ agentId, projectId }: Props) {
229
257
  const [loading, setLoading] = useState(false);
230
258
  const [error, setError] = useState<string | null>(null);
231
259
  const [busyId, setBusyId] = useState<string | null>(null);
232
- const [nonce, setNonce] = useState(0);
260
+ // Per record: a refused review refetches only ITS card's preview.
261
+ const [nonces, setNonces] = useState<Readonly<Record<string, number>>>({});
262
+ const latest = useRef({ nonces, revisions }); latest.current = { nonces, revisions };
263
+ const refetch = useCallback(async (rec: PendingChangeRecord): Promise<PendingPreview> => {
264
+ refetches.current.set(rec.id, null);
265
+ try {
266
+ const listed = (await relisted.current.promise)?.find(r => r.id === rec.id) ?? rec;
267
+ const fresh = await previewPendingChange({ agentId, ...(projectId ? { projectId } : {}), id: rec.id }, client);
268
+ const revision = (latest.current.nonces[rec.id] ?? 0) + 1 + (latest.current.revisions[listed.sourceSlug] ?? 0);
269
+ refetches.current.set(rec.id, { key: previewKey(listed, revision), preview: fresh });
270
+ previews.current.set(rec.id, fresh);
271
+ return fresh;
272
+ } catch (failure) { refetches.current.delete(rec.id); throw failure; }
273
+ finally { setNonces(prev => ({ ...prev, [rec.id]: (prev[rec.id] ?? 0) + 1 })); }
274
+ }, [agentId, projectId]);
233
275
  const [focusIndex, setFocusIndex] = useState(0);
234
276
  const [review, setReview] = useState<ReviewRequest | null>(null);
235
277
  const [results, setResults] = useState<ReviewResult[]>([]);
@@ -250,7 +292,7 @@ export function PendingChangesPanel({ agentId, projectId }: Props) {
250
292
  setLoading(true); setError(null);
251
293
  try {
252
294
  const list = await listPendingChanges({ agentId, ...(projectId ? { projectId } : {}),
253
- ...(convFilter ? { conversationId: convFilter } : {}), ...(withOrphans ? { include_orphans: true } : {}) });
295
+ ...(convFilter ? { conversationId: convFilter } : {}), ...(withOrphans ? { include_orphans: true } : {}) }, client);
254
296
  if (mine !== listSeq.current) return undefined;
255
297
  const ids = new Set(list.changes.map(rec => rec.id));
256
298
  setChanges(list.changes);
@@ -277,6 +319,7 @@ export function PendingChangesPanel({ agentId, projectId }: Props) {
277
319
  const firstRevision = useRef(true);
278
320
  useEffect(() => {
279
321
  if (firstRevision.current) { firstRevision.current = false; return; }
322
+ if (revisions === listedRevisions.current) return;
280
323
  void fetchPendingRef.current(false);
281
324
  }, [revisions]);
282
325
 
@@ -291,7 +334,11 @@ export function PendingChangesPanel({ agentId, projectId }: Props) {
291
334
  const groupsVisible = useMemo(() => visibleGitGroups(gitGroups, filters), [gitGroups, filters]);
292
335
  const gitRows = useMemo(() => currentGitRows(groupsVisible), [groupsVisible]);
293
336
  const items = useMemo(() => buildChangeItems(visible, gitRows, filters.kind, view), [visible, gitRows, filters.kind, view]);
294
- const { approvable, needsAck } = useMemo(() => splitApprovable(visible, orphanIds, REVIEW_BATCH), [visible, orphanIds]);
337
+ // A landed preview that needs an acknowledgement moves its record into the count (no fetch of its own).
338
+ const [ackPreviews, setAckPreviews] = useState(0);
339
+ const { approvable, needsAck } = useMemo(() => splitApprovable(visible, orphanIds, REVIEW_BATCH, peekPreview),
340
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- `ackPreviews` re-reads the preview map
341
+ [visible, orphanIds, peekPreview, ackPreviews]);
295
342
  // The records one review session already acted on: the next batch starts after them,
296
343
  // so a refused record does not come straight back.
297
344
  const reviewed = useRef(new Set<string>());
@@ -328,28 +375,33 @@ export function PendingChangesPanel({ agentId, projectId }: Props) {
328
375
  */
329
376
  const dropResolvedDeleteRow = useCallback((rec: PendingChangeRecord) => {
330
377
  if (rec.op !== 'delete' || !rec.uri) return;
331
- const node = useFilesystemStore.getState().files[rec.uri];
378
+ const node = storeApi.getState().files[rec.uri];
332
379
  if (!node || node.status === 'deleted') removeFilePath(rec.uri);
333
- }, [removeFilePath]);
380
+ }, [removeFilePath, storeApi]);
334
381
 
335
382
  const afterMutation = useCallback(async (done: ReviewResult[]) => {
336
383
  for (const result of done) {
337
384
  const rec = changes.find(c => c.id === result.id);
338
385
  if (rec && result.outcome === 'ok' && result.action !== 'revert') dropResolvedDeleteRow(rec);
339
- if (result.outcome !== 'ok') previews.current.delete(result.id);
386
+ // A `needs-ack` answer came from a still-valid preview: it stays cached (the header counts it).
387
+ if (result.outcome !== 'ok' && result.outcome !== 'needs-ack' && !refetches.current.has(result.id)) previews.current.delete(result.id);
340
388
  }
341
- if (done.some(r => r.outcome !== 'ok')) setNonce(value => value + 1);
389
+ const refused = done.filter(r => r.outcome !== 'ok' && r.outcome !== 'needs-ack' && !refetches.current.has(r.id));
390
+ if (refused.length) setNonces(prev => { const next = { ...prev }; for (const r of refused) next[r.id] = (next[r.id] ?? 0) + 1; return next; });
342
391
  setResults(done);
343
- await fetchPending(false);
392
+ const after = await fetchPending(false);
393
+ // The STORE's value, not the last render's: the batch's own bumps may not have rendered yet.
394
+ listedRevisions.current = storeApi.getState().sourceChangeRevision; setHeld(null);
395
+ relisted.current.resolve(after); relisted.current = Promise.withResolvers();
344
396
  void refreshPendingCount({ agentId, projectId });
345
- }, [changes, dropResolvedDeleteRow, fetchPending, refreshPendingCount, agentId, projectId]);
397
+ }, [changes, dropResolvedDeleteRow, fetchPending, refreshPendingCount, agentId, projectId, storeApi]);
346
398
 
347
399
  const resolveReview = useCallback(async (rec: PendingChangeRecord, action: ReviewAction) => {
348
400
  if (!canWrite) return;
349
401
  const preview = previews.current.get(rec.id);
350
402
  if (!preview || preview.requiresAcknowledgement.length) { setReview({ kind: 'single', rec, action, ...(preview ? { preview } : {}) }); return; }
351
403
  setBusyId(rec.id);
352
- try { await afterMutation([await applyReview({ agentId, projectId }, rec, preview, action, false)]); }
404
+ try { await afterMutation([await applyReview({ agentId, projectId, client }, rec, preview, action, false)]); }
353
405
  finally { setBusyId(null); }
354
406
  }, [canWrite, agentId, projectId, afterMutation]);
355
407
 
@@ -357,7 +409,7 @@ export function PendingChangesPanel({ agentId, projectId }: Props) {
357
409
  const discard = useCallback(async (rec: PendingChangeRecord) => {
358
410
  setBusyId(rec.id);
359
411
  try {
360
- const res = await discardPendingChange({ agentId, ...(projectId ? { projectId } : {}), id: rec.id });
412
+ const res = await discardPendingChange({ agentId, ...(projectId ? { projectId } : {}), id: rec.id }, client);
361
413
  if (!res.ok) setError(res.error || 'Discard failed'); else dropResolvedDeleteRow(rec);
362
414
  await fetchPending(false);
363
415
  void refreshPendingCount({ agentId, projectId });
@@ -370,7 +422,7 @@ export function PendingChangesPanel({ agentId, projectId }: Props) {
370
422
  if (repairTimer.current) { clearTimeout(repairTimer.current); repairTimer.current = null; }
371
423
  setRepairing(true); setConfirmRepair(false); setError(null);
372
424
  try {
373
- await discardOrphanedPending({ agentId, ...(projectId ? { projectId } : {}), ...(convFilter ? { conversationId: convFilter } : {}) });
425
+ await discardOrphanedPending({ agentId, ...(projectId ? { projectId } : {}), ...(convFilter ? { conversationId: convFilter } : {}) }, client);
374
426
  const candidates = changes.filter((rec) => orphanIds.has(rec.id));
375
427
  const after = await fetchPending(true);
376
428
  if (after) {
@@ -388,22 +440,30 @@ export function PendingChangesPanel({ agentId, projectId }: Props) {
388
440
  }, [confirmRepair, repair]); useEffect(() => () => { if (repairTimer.current) clearTimeout(repairTimer.current); }, []);
389
441
 
390
442
  // Approve the visible records whose own fields need no acknowledgement (two-step
391
- // confirm). A record whose fresh preview turns out to need one is reported, not skipped silently.
443
+ // confirm). A record whose fresh preview turns out to need one is reported AND handed
444
+ // to the grouped review dialog, where it is approved or dismissed with its acknowledgement.
392
445
  const approveAll = useCallback(async () => {
393
- setApprovingAll(true); setConfirmAll(false); setError(null);
446
+ setApprovingAll(true); setConfirmAll(false); setError(null); holdRevisions();
394
447
  const done: ReviewResult[] = [];
395
448
  try {
396
449
  for (const rec of approvable) {
397
450
  let preview = previews.current.get(rec.id);
398
451
  if (!preview) {
399
- try { preview = await previewPendingChange({ agentId, ...(projectId ? { projectId } : {}), id: rec.id }); }
452
+ try { preview = await previewPendingChange({ agentId, ...(projectId ? { projectId } : {}), id: rec.id }, client); }
400
453
  catch (e) { done.push({ id: rec.id, uri: rec.uri, action: 'approve', outcome: 'error', detail: e instanceof Error ? e.message : 'Preview failed' }); continue; }
454
+ previews.current.set(rec.id, preview);
401
455
  }
402
456
  if (preview.requiresAcknowledgement.length) { done.push({ id: rec.id, uri: rec.uri, action: 'approve', outcome: 'needs-ack' }); continue; }
403
- done.push(await applyReview({ agentId, projectId }, rec, preview, 'approve', false));
457
+ done.push(await applyReview({ agentId, projectId, client }, rec, preview, 'approve', false));
404
458
  }
405
- } finally { setApprovingAll(false); await afterMutation(done); }
406
- }, [approvable, agentId, projectId, afterMutation]);
459
+ } finally {
460
+ setApprovingAll(false);
461
+ const handed = approvable.filter(rec => done.some(r => r.id === rec.id && r.outcome === 'needs-ack'));
462
+ if (handed.length) setAckPreviews(n => n + 1);
463
+ await afterMutation(done);
464
+ if (handed.length && canWrite) { reviewed.current.clear(); setReview({ kind: 'group', records: handed.slice(0, REVIEW_BATCH), total: handed.length }); }
465
+ }
466
+ }, [approvable, agentId, projectId, afterMutation, canWrite]);
407
467
 
408
468
  const move = useCallback((delta: number) => setFocusIndex((i) => (visible.length === 0 ? 0
409
469
  : Math.max(0, Math.min(visible.length - 1, i + delta)))), [visible.length]);
@@ -437,8 +497,8 @@ export function PendingChangesPanel({ agentId, projectId }: Props) {
437
497
  if (item.kind === 'pending-card') {
438
498
  return <PendingDiffCard rec={rec} agentId={agentId} projectId={projectId} focused={index === focusIndex} orphaned={orphaned}
439
499
  onOpen={openInTree} {...handlers} onFocus={() => setFocusIndex(index)} busy={busyId === rec.id} queue={queue}
440
- revision={nonce + (revisions[rec.sourceSlug] ?? 0)} canWrite={canWrite}
441
- onPreview={preview => { previews.current.set(rec.id, preview); }} peekPreview={peekPreview}
500
+ revision={(nonces[rec.id] ?? 0) + (revisions[rec.sourceSlug] ?? 0)} canWrite={canWrite}
501
+ onPreview={preview => { previews.current.set(rec.id, preview); if (preview.requiresAcknowledgement.length) setAckPreviews(n => n + 1); }} peekPreview={peekPreview} takeRefetch={takeRefetch}
442
502
  extra={filters.kind === 'all' ? <CurrentGitDiffs uri={rec.uri} rows={gitRows} agentId={agentId} projectId={projectId} canWrite={canWrite} revisions={revisions} queue={queue} /> : null} />;
443
503
  }
444
504
  return <div onMouseDown={() => setFocusIndex(index)} className={`group h-8 flex items-center gap-2 px-2 rounded-lg ${
@@ -461,7 +521,7 @@ export function PendingChangesPanel({ agentId, projectId }: Props) {
461
521
  {canWrite && review ? <PendingReviewDialog request={review} agentId={agentId} projectId={projectId} run={queue.run}
462
522
  key={review.kind === 'group' ? `group\u0000${review.records[0]?.id ?? ''}` : `single\u0000${review.rec.id}`}
463
523
  onClose={() => setReview(null)} next={review.kind === 'group' ? unreviewed.filter(rec => !review.records.some(r => r.id === rec.id)).length : 0}
464
- onNext={openReviewBatch}
524
+ onNext={openReviewBatch} refetch={refetch} onBatchStart={holdRevisions}
465
525
  onDone={done => { for (const result of done) reviewed.current.add(result.id); void afterMutation(done); }} /> : null}
466
526
  {/* Header: title + nav cursor + approve-all + view toggle + refresh */}
467
527
  <div className="flex flex-wrap items-center gap-1.5 px-3 py-2 border-b border-white/5 shrink-0">
@@ -548,7 +608,7 @@ export function PendingChangesPanel({ agentId, projectId }: Props) {
548
608
  <div className="mx-3 mt-2 max-h-28 overflow-auto overlay-scroll rounded-lg border border-amber-500/25 bg-amber-500/[0.06] px-2.5 py-1.5 text-[10.5px] text-amber-100/85 shrink-0">
549
609
  <div className="flex items-center gap-2 font-semibold">{results.length - failed.length} done · {failed.length} not done
550
610
  <button type="button" onClick={() => setResults([])} className="ml-auto rounded hover:bg-white/10" title="Dismiss"><X className="w-3 h-3" /></button></div>
551
- {failed.map(r => <div key={r.id} className="truncate font-mono" title={r.detail}>{r.action} {r.uri}: {r.outcome === 'changed' ? 'changed since the preview (409)' : r.outcome === 'needs-ack' ? 'needs confirmation' : r.detail ?? 'refused'}</div>)}
611
+ {failed.map(r => <div key={r.id} className="truncate font-mono" title={r.detail}>{r.action} {r.uri}: {r.outcome === 'changed' ? 'changed since the preview (409)' : r.outcome === 'needs-ack' ? 'needs confirmation — opened in the review dialog' : r.detail ?? 'refused'}</div>)}
552
612
  </div>
553
613
  ) : null}
554
614