opentasks 0.0.6 → 0.0.7

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 (660) hide show
  1. package/README.md +68 -0
  2. package/dist/__tests__/cli-tools.test.d.ts +8 -0
  3. package/dist/__tests__/cli-tools.test.d.ts.map +1 -0
  4. package/dist/__tests__/cli-tools.test.js +546 -0
  5. package/dist/__tests__/cli-tools.test.js.map +1 -0
  6. package/dist/__tests__/cli.test.d.ts +5 -0
  7. package/dist/__tests__/cli.test.d.ts.map +1 -0
  8. package/dist/__tests__/cli.test.js +77 -0
  9. package/dist/__tests__/cli.test.js.map +1 -0
  10. package/dist/__tests__/p1-p3-gaps.test.d.ts +2 -0
  11. package/dist/__tests__/p1-p3-gaps.test.d.ts.map +1 -0
  12. package/dist/__tests__/p1-p3-gaps.test.js +463 -0
  13. package/dist/__tests__/p1-p3-gaps.test.js.map +1 -0
  14. package/dist/cli.d.ts +1 -0
  15. package/dist/cli.d.ts.map +1 -1
  16. package/dist/cli.js +64 -0
  17. package/dist/cli.js.map +1 -1
  18. package/dist/client/__tests__/client-crud.test.d.ts +7 -0
  19. package/dist/client/__tests__/client-crud.test.d.ts.map +1 -0
  20. package/dist/client/__tests__/client-crud.test.js +404 -0
  21. package/dist/client/__tests__/client-crud.test.js.map +1 -0
  22. package/dist/client/__tests__/client.test.d.ts +5 -0
  23. package/dist/client/__tests__/client.test.d.ts.map +1 -0
  24. package/dist/client/__tests__/client.test.js +518 -0
  25. package/dist/client/__tests__/client.test.js.map +1 -0
  26. package/dist/client/client.d.ts +47 -1
  27. package/dist/client/client.d.ts.map +1 -1
  28. package/dist/client/client.js +71 -0
  29. package/dist/client/client.js.map +1 -1
  30. package/dist/client/index.d.ts +1 -1
  31. package/dist/client/index.d.ts.map +1 -1
  32. package/dist/config/__tests__/defaults.test.d.ts +2 -0
  33. package/dist/config/__tests__/defaults.test.d.ts.map +1 -0
  34. package/dist/config/__tests__/defaults.test.js +57 -0
  35. package/dist/config/__tests__/defaults.test.js.map +1 -0
  36. package/dist/config/__tests__/env.test.d.ts +2 -0
  37. package/dist/config/__tests__/env.test.d.ts.map +1 -0
  38. package/dist/config/__tests__/env.test.js +136 -0
  39. package/dist/config/__tests__/env.test.js.map +1 -0
  40. package/dist/config/__tests__/index.test.d.ts +2 -0
  41. package/dist/config/__tests__/index.test.d.ts.map +1 -0
  42. package/dist/config/__tests__/index.test.js +113 -0
  43. package/dist/config/__tests__/index.test.js.map +1 -0
  44. package/dist/config/__tests__/loader.test.d.ts +2 -0
  45. package/dist/config/__tests__/loader.test.d.ts.map +1 -0
  46. package/dist/config/__tests__/loader.test.js +128 -0
  47. package/dist/config/__tests__/loader.test.js.map +1 -0
  48. package/dist/config/__tests__/merge.test.d.ts +2 -0
  49. package/dist/config/__tests__/merge.test.d.ts.map +1 -0
  50. package/dist/config/__tests__/merge.test.js +79 -0
  51. package/dist/config/__tests__/merge.test.js.map +1 -0
  52. package/dist/config/__tests__/schema.test.d.ts +2 -0
  53. package/dist/config/__tests__/schema.test.d.ts.map +1 -0
  54. package/dist/config/__tests__/schema.test.js +300 -0
  55. package/dist/config/__tests__/schema.test.js.map +1 -0
  56. package/dist/config/schema.d.ts +178 -4
  57. package/dist/config/schema.d.ts.map +1 -1
  58. package/dist/config/schema.js +109 -7
  59. package/dist/config/schema.js.map +1 -1
  60. package/dist/context-files/context-files.d.ts +72 -0
  61. package/dist/context-files/context-files.d.ts.map +1 -0
  62. package/dist/context-files/context-files.js +145 -0
  63. package/dist/context-files/context-files.js.map +1 -0
  64. package/dist/context-files/index.d.ts +16 -0
  65. package/dist/context-files/index.d.ts.map +1 -0
  66. package/dist/context-files/index.js +14 -0
  67. package/dist/context-files/index.js.map +1 -0
  68. package/dist/context-files/resolver.d.ts +43 -0
  69. package/dist/context-files/resolver.d.ts.map +1 -0
  70. package/dist/context-files/resolver.js +127 -0
  71. package/dist/context-files/resolver.js.map +1 -0
  72. package/dist/context-files/types.d.ts +94 -0
  73. package/dist/context-files/types.d.ts.map +1 -0
  74. package/dist/context-files/types.js +10 -0
  75. package/dist/context-files/types.js.map +1 -0
  76. package/dist/context-files/watcher-integration.d.ts +47 -0
  77. package/dist/context-files/watcher-integration.d.ts.map +1 -0
  78. package/dist/context-files/watcher-integration.js +47 -0
  79. package/dist/context-files/watcher-integration.js.map +1 -0
  80. package/dist/core/__tests__/conditional-redirects.test.d.ts +2 -0
  81. package/dist/core/__tests__/conditional-redirects.test.d.ts.map +1 -0
  82. package/dist/core/__tests__/conditional-redirects.test.js +83 -0
  83. package/dist/core/__tests__/conditional-redirects.test.js.map +1 -0
  84. package/dist/core/__tests__/connections.test.d.ts +2 -0
  85. package/dist/core/__tests__/connections.test.d.ts.map +1 -0
  86. package/dist/core/__tests__/connections.test.js +158 -0
  87. package/dist/core/__tests__/connections.test.js.map +1 -0
  88. package/dist/core/__tests__/hash.test.d.ts +2 -0
  89. package/dist/core/__tests__/hash.test.d.ts.map +1 -0
  90. package/dist/core/__tests__/hash.test.js +139 -0
  91. package/dist/core/__tests__/hash.test.js.map +1 -0
  92. package/dist/core/__tests__/id.test.d.ts +2 -0
  93. package/dist/core/__tests__/id.test.d.ts.map +1 -0
  94. package/dist/core/__tests__/id.test.js +142 -0
  95. package/dist/core/__tests__/id.test.js.map +1 -0
  96. package/dist/core/__tests__/location.test.d.ts +2 -0
  97. package/dist/core/__tests__/location.test.d.ts.map +1 -0
  98. package/dist/core/__tests__/location.test.js +77 -0
  99. package/dist/core/__tests__/location.test.js.map +1 -0
  100. package/dist/core/__tests__/merge-driver.test.d.ts +2 -0
  101. package/dist/core/__tests__/merge-driver.test.d.ts.map +1 -0
  102. package/dist/core/__tests__/merge-driver.test.js +218 -0
  103. package/dist/core/__tests__/merge-driver.test.js.map +1 -0
  104. package/dist/core/__tests__/redirects.test.d.ts +2 -0
  105. package/dist/core/__tests__/redirects.test.d.ts.map +1 -0
  106. package/dist/core/__tests__/redirects.test.js +123 -0
  107. package/dist/core/__tests__/redirects.test.js.map +1 -0
  108. package/dist/core/__tests__/resolve-location-target.test.d.ts +8 -0
  109. package/dist/core/__tests__/resolve-location-target.test.d.ts.map +1 -0
  110. package/dist/core/__tests__/resolve-location-target.test.js +303 -0
  111. package/dist/core/__tests__/resolve-location-target.test.js.map +1 -0
  112. package/dist/core/__tests__/uri.test.d.ts +2 -0
  113. package/dist/core/__tests__/uri.test.d.ts.map +1 -0
  114. package/dist/core/__tests__/uri.test.js +159 -0
  115. package/dist/core/__tests__/uri.test.js.map +1 -0
  116. package/dist/core/__tests__/worktree.test.d.ts +2 -0
  117. package/dist/core/__tests__/worktree.test.d.ts.map +1 -0
  118. package/dist/core/__tests__/worktree.test.js +120 -0
  119. package/dist/core/__tests__/worktree.test.js.map +1 -0
  120. package/dist/core/merge-driver.d.ts +5 -0
  121. package/dist/core/merge-driver.d.ts.map +1 -1
  122. package/dist/core/merge-driver.js +35 -0
  123. package/dist/core/merge-driver.js.map +1 -1
  124. package/dist/daemon/__tests__/flush.test.d.ts +5 -0
  125. package/dist/daemon/__tests__/flush.test.d.ts.map +1 -0
  126. package/dist/daemon/__tests__/flush.test.js +213 -0
  127. package/dist/daemon/__tests__/flush.test.js.map +1 -0
  128. package/dist/daemon/__tests__/integration.test.d.ts +7 -0
  129. package/dist/daemon/__tests__/integration.test.d.ts.map +1 -0
  130. package/dist/daemon/__tests__/integration.test.js +276 -0
  131. package/dist/daemon/__tests__/integration.test.js.map +1 -0
  132. package/dist/daemon/__tests__/ipc.test.d.ts +5 -0
  133. package/dist/daemon/__tests__/ipc.test.d.ts.map +1 -0
  134. package/dist/daemon/__tests__/ipc.test.js +314 -0
  135. package/dist/daemon/__tests__/ipc.test.js.map +1 -0
  136. package/dist/daemon/__tests__/lifecycle.test.d.ts +5 -0
  137. package/dist/daemon/__tests__/lifecycle.test.d.ts.map +1 -0
  138. package/dist/daemon/__tests__/lifecycle.test.js +301 -0
  139. package/dist/daemon/__tests__/lifecycle.test.js.map +1 -0
  140. package/dist/daemon/__tests__/lock.test.d.ts +5 -0
  141. package/dist/daemon/__tests__/lock.test.d.ts.map +1 -0
  142. package/dist/daemon/__tests__/lock.test.js +192 -0
  143. package/dist/daemon/__tests__/lock.test.js.map +1 -0
  144. package/dist/daemon/__tests__/methods/graph.test.d.ts +5 -0
  145. package/dist/daemon/__tests__/methods/graph.test.d.ts.map +1 -0
  146. package/dist/daemon/__tests__/methods/graph.test.js +309 -0
  147. package/dist/daemon/__tests__/methods/graph.test.js.map +1 -0
  148. package/dist/daemon/__tests__/methods/provider.test.d.ts +7 -0
  149. package/dist/daemon/__tests__/methods/provider.test.d.ts.map +1 -0
  150. package/dist/daemon/__tests__/methods/provider.test.js +181 -0
  151. package/dist/daemon/__tests__/methods/provider.test.js.map +1 -0
  152. package/dist/daemon/__tests__/methods/tools.test.d.ts +5 -0
  153. package/dist/daemon/__tests__/methods/tools.test.d.ts.map +1 -0
  154. package/dist/daemon/__tests__/methods/tools.test.js +587 -0
  155. package/dist/daemon/__tests__/methods/tools.test.js.map +1 -0
  156. package/dist/daemon/__tests__/multi-location.test.d.ts +8 -0
  157. package/dist/daemon/__tests__/multi-location.test.d.ts.map +1 -0
  158. package/dist/daemon/__tests__/multi-location.test.js +669 -0
  159. package/dist/daemon/__tests__/multi-location.test.js.map +1 -0
  160. package/dist/daemon/__tests__/registry.test.d.ts +5 -0
  161. package/dist/daemon/__tests__/registry.test.d.ts.map +1 -0
  162. package/dist/daemon/__tests__/registry.test.js +208 -0
  163. package/dist/daemon/__tests__/registry.test.js.map +1 -0
  164. package/dist/daemon/__tests__/watcher.test.d.ts +5 -0
  165. package/dist/daemon/__tests__/watcher.test.d.ts.map +1 -0
  166. package/dist/daemon/__tests__/watcher.test.js +234 -0
  167. package/dist/daemon/__tests__/watcher.test.js.map +1 -0
  168. package/dist/daemon/index.d.ts +6 -4
  169. package/dist/daemon/index.d.ts.map +1 -1
  170. package/dist/daemon/index.js +3 -2
  171. package/dist/daemon/index.js.map +1 -1
  172. package/dist/daemon/ipc.d.ts.map +1 -1
  173. package/dist/daemon/ipc.js +8 -1
  174. package/dist/daemon/ipc.js.map +1 -1
  175. package/dist/daemon/lifecycle.d.ts.map +1 -1
  176. package/dist/daemon/lifecycle.js +138 -25
  177. package/dist/daemon/lifecycle.js.map +1 -1
  178. package/dist/daemon/location-state.d.ts +12 -6
  179. package/dist/daemon/location-state.d.ts.map +1 -1
  180. package/dist/daemon/location-state.js +50 -20
  181. package/dist/daemon/location-state.js.map +1 -1
  182. package/dist/daemon/methods/__tests__/graph.test.d.ts +5 -0
  183. package/dist/daemon/methods/__tests__/graph.test.d.ts.map +1 -0
  184. package/dist/daemon/methods/__tests__/graph.test.js +274 -0
  185. package/dist/daemon/methods/__tests__/graph.test.js.map +1 -0
  186. package/dist/daemon/methods/__tests__/provider.test.d.ts +5 -0
  187. package/dist/daemon/methods/__tests__/provider.test.d.ts.map +1 -0
  188. package/dist/daemon/methods/__tests__/provider.test.js +184 -0
  189. package/dist/daemon/methods/__tests__/provider.test.js.map +1 -0
  190. package/dist/daemon/methods/__tests__/tools.test.d.ts +5 -0
  191. package/dist/daemon/methods/__tests__/tools.test.d.ts.map +1 -0
  192. package/dist/daemon/methods/__tests__/tools.test.js +295 -0
  193. package/dist/daemon/methods/__tests__/tools.test.js.map +1 -0
  194. package/dist/daemon/methods/context-files.d.ts +14 -0
  195. package/dist/daemon/methods/context-files.d.ts.map +1 -0
  196. package/dist/daemon/methods/context-files.js +95 -0
  197. package/dist/daemon/methods/context-files.js.map +1 -0
  198. package/dist/daemon/methods/provider.d.ts.map +1 -1
  199. package/dist/daemon/methods/provider.js +15 -0
  200. package/dist/daemon/methods/provider.js.map +1 -1
  201. package/dist/daemon/methods/tools.d.ts +1 -1
  202. package/dist/daemon/methods/tools.d.ts.map +1 -1
  203. package/dist/daemon/methods/tools.js +3 -2
  204. package/dist/daemon/methods/tools.js.map +1 -1
  205. package/dist/daemon/sessionlog-linker.d.ts +71 -0
  206. package/dist/daemon/sessionlog-linker.d.ts.map +1 -0
  207. package/dist/daemon/sessionlog-linker.js +472 -0
  208. package/dist/daemon/sessionlog-linker.js.map +1 -0
  209. package/dist/daemon/sessionlog-watcher.d.ts +79 -0
  210. package/dist/daemon/sessionlog-watcher.d.ts.map +1 -0
  211. package/dist/daemon/sessionlog-watcher.js +289 -0
  212. package/dist/daemon/sessionlog-watcher.js.map +1 -0
  213. package/dist/graph/__tests__/EdgeTypeRegistry.test.d.ts +2 -0
  214. package/dist/graph/__tests__/EdgeTypeRegistry.test.d.ts.map +1 -0
  215. package/dist/graph/__tests__/EdgeTypeRegistry.test.js +212 -0
  216. package/dist/graph/__tests__/EdgeTypeRegistry.test.js.map +1 -0
  217. package/dist/graph/__tests__/FederatedGraph.test.d.ts +2 -0
  218. package/dist/graph/__tests__/FederatedGraph.test.d.ts.map +1 -0
  219. package/dist/graph/__tests__/FederatedGraph.test.js +661 -0
  220. package/dist/graph/__tests__/FederatedGraph.test.js.map +1 -0
  221. package/dist/graph/__tests__/GraphologyAdapter.test.d.ts +2 -0
  222. package/dist/graph/__tests__/GraphologyAdapter.test.d.ts.map +1 -0
  223. package/dist/graph/__tests__/GraphologyAdapter.test.js +326 -0
  224. package/dist/graph/__tests__/GraphologyAdapter.test.js.map +1 -0
  225. package/dist/graph/__tests__/HydratingFederatedGraph.test.d.ts +2 -0
  226. package/dist/graph/__tests__/HydratingFederatedGraph.test.d.ts.map +1 -0
  227. package/dist/graph/__tests__/HydratingFederatedGraph.test.js +587 -0
  228. package/dist/graph/__tests__/HydratingFederatedGraph.test.js.map +1 -0
  229. package/dist/graph/__tests__/debounce.test.d.ts +5 -0
  230. package/dist/graph/__tests__/debounce.test.d.ts.map +1 -0
  231. package/dist/graph/__tests__/debounce.test.js +195 -0
  232. package/dist/graph/__tests__/debounce.test.js.map +1 -0
  233. package/dist/graph/__tests__/edge-cases.test.d.ts +8 -0
  234. package/dist/graph/__tests__/edge-cases.test.d.ts.map +1 -0
  235. package/dist/graph/__tests__/edge-cases.test.js +472 -0
  236. package/dist/graph/__tests__/edge-cases.test.js.map +1 -0
  237. package/dist/graph/__tests__/expansion.test.d.ts +2 -0
  238. package/dist/graph/__tests__/expansion.test.d.ts.map +1 -0
  239. package/dist/graph/__tests__/expansion.test.js +105 -0
  240. package/dist/graph/__tests__/expansion.test.js.map +1 -0
  241. package/dist/graph/__tests__/provider-store.test.d.ts +5 -0
  242. package/dist/graph/__tests__/provider-store.test.d.ts.map +1 -0
  243. package/dist/graph/__tests__/provider-store.test.js +791 -0
  244. package/dist/graph/__tests__/provider-store.test.js.map +1 -0
  245. package/dist/graph/__tests__/query.test.d.ts +5 -0
  246. package/dist/graph/__tests__/query.test.d.ts.map +1 -0
  247. package/dist/graph/__tests__/query.test.js +774 -0
  248. package/dist/graph/__tests__/query.test.js.map +1 -0
  249. package/dist/graph/__tests__/store.test.d.ts +5 -0
  250. package/dist/graph/__tests__/store.test.d.ts.map +1 -0
  251. package/dist/graph/__tests__/store.test.js +489 -0
  252. package/dist/graph/__tests__/store.test.js.map +1 -0
  253. package/dist/graph/__tests__/sync.test.d.ts +5 -0
  254. package/dist/graph/__tests__/sync.test.d.ts.map +1 -0
  255. package/dist/graph/__tests__/sync.test.js +129 -0
  256. package/dist/graph/__tests__/sync.test.js.map +1 -0
  257. package/dist/graph/__tests__/validation.test.d.ts +2 -0
  258. package/dist/graph/__tests__/validation.test.d.ts.map +1 -0
  259. package/dist/graph/__tests__/validation.test.js +521 -0
  260. package/dist/graph/__tests__/validation.test.js.map +1 -0
  261. package/dist/graph/index.d.ts +1 -1
  262. package/dist/graph/index.d.ts.map +1 -1
  263. package/dist/graph/index.js.map +1 -1
  264. package/dist/graph/provider-store.d.ts +78 -4
  265. package/dist/graph/provider-store.d.ts.map +1 -1
  266. package/dist/graph/provider-store.js +579 -55
  267. package/dist/graph/provider-store.js.map +1 -1
  268. package/dist/graph/store.d.ts.map +1 -1
  269. package/dist/graph/store.js +3 -0
  270. package/dist/graph/store.js.map +1 -1
  271. package/dist/graph/types.d.ts +16 -0
  272. package/dist/graph/types.d.ts.map +1 -1
  273. package/dist/graph/types.js.map +1 -1
  274. package/dist/index.d.ts +4 -2
  275. package/dist/index.d.ts.map +1 -1
  276. package/dist/index.js +9 -3
  277. package/dist/index.js.map +1 -1
  278. package/dist/materialization/git-archive-store.js +2 -2
  279. package/dist/materialization/git-archive-store.js.map +1 -1
  280. package/dist/materialization/git-remote-store.js +2 -2
  281. package/dist/materialization/git-remote-store.js.map +1 -1
  282. package/dist/materialization/snapshot.js +4 -4
  283. package/dist/materialization/snapshot.js.map +1 -1
  284. package/dist/mcp/index.d.ts +8 -0
  285. package/dist/mcp/index.d.ts.map +1 -0
  286. package/dist/mcp/index.js +8 -0
  287. package/dist/mcp/index.js.map +1 -0
  288. package/dist/mcp/server.d.ts +39 -0
  289. package/dist/mcp/server.d.ts.map +1 -0
  290. package/dist/mcp/server.js +677 -0
  291. package/dist/mcp/server.js.map +1 -0
  292. package/dist/mcp/stdio.d.ts +14 -0
  293. package/dist/mcp/stdio.d.ts.map +1 -0
  294. package/dist/mcp/stdio.js +19 -0
  295. package/dist/mcp/stdio.js.map +1 -0
  296. package/dist/providers/__tests__/beads.test.d.ts +5 -0
  297. package/dist/providers/__tests__/beads.test.d.ts.map +1 -0
  298. package/dist/providers/__tests__/beads.test.js +591 -0
  299. package/dist/providers/__tests__/beads.test.js.map +1 -0
  300. package/dist/providers/__tests__/claude-tasks.test.d.ts +5 -0
  301. package/dist/providers/__tests__/claude-tasks.test.d.ts.map +1 -0
  302. package/dist/providers/__tests__/claude-tasks.test.js +392 -0
  303. package/dist/providers/__tests__/claude-tasks.test.js.map +1 -0
  304. package/dist/providers/__tests__/from-config.test.d.ts +5 -0
  305. package/dist/providers/__tests__/from-config.test.d.ts.map +1 -0
  306. package/dist/providers/__tests__/from-config.test.js +152 -0
  307. package/dist/providers/__tests__/from-config.test.js.map +1 -0
  308. package/dist/providers/__tests__/materialization.test.d.ts +5 -0
  309. package/dist/providers/__tests__/materialization.test.d.ts.map +1 -0
  310. package/dist/providers/__tests__/materialization.test.js +407 -0
  311. package/dist/providers/__tests__/materialization.test.js.map +1 -0
  312. package/dist/providers/__tests__/native.test.d.ts +5 -0
  313. package/dist/providers/__tests__/native.test.d.ts.map +1 -0
  314. package/dist/providers/__tests__/native.test.js +566 -0
  315. package/dist/providers/__tests__/native.test.js.map +1 -0
  316. package/dist/providers/__tests__/registry.test.d.ts +5 -0
  317. package/dist/providers/__tests__/registry.test.d.ts.map +1 -0
  318. package/dist/providers/__tests__/registry.test.js +183 -0
  319. package/dist/providers/__tests__/registry.test.js.map +1 -0
  320. package/dist/providers/beads.d.ts.map +1 -1
  321. package/dist/providers/beads.js +17 -1
  322. package/dist/providers/beads.js.map +1 -1
  323. package/dist/providers/claude-tasks-fs.d.ts +22 -0
  324. package/dist/providers/claude-tasks-fs.d.ts.map +1 -0
  325. package/dist/providers/claude-tasks-fs.js +158 -0
  326. package/dist/providers/claude-tasks-fs.js.map +1 -0
  327. package/dist/providers/claude-tasks.d.ts +13 -4
  328. package/dist/providers/claude-tasks.d.ts.map +1 -1
  329. package/dist/providers/claude-tasks.js +318 -5
  330. package/dist/providers/claude-tasks.js.map +1 -1
  331. package/dist/providers/from-config.d.ts.map +1 -1
  332. package/dist/providers/from-config.js +35 -17
  333. package/dist/providers/from-config.js.map +1 -1
  334. package/dist/providers/global.d.ts.map +1 -1
  335. package/dist/providers/global.js +5 -0
  336. package/dist/providers/global.js.map +1 -1
  337. package/dist/providers/index.d.ts +6 -4
  338. package/dist/providers/index.d.ts.map +1 -1
  339. package/dist/providers/index.js +4 -3
  340. package/dist/providers/index.js.map +1 -1
  341. package/dist/providers/map.d.ts.map +1 -1
  342. package/dist/providers/map.js +5 -0
  343. package/dist/providers/map.js.map +1 -1
  344. package/dist/providers/materialization.d.ts +23 -5
  345. package/dist/providers/materialization.d.ts.map +1 -1
  346. package/dist/providers/materialization.js +95 -4
  347. package/dist/providers/materialization.js.map +1 -1
  348. package/dist/providers/native.d.ts.map +1 -1
  349. package/dist/providers/native.js +5 -0
  350. package/dist/providers/native.js.map +1 -1
  351. package/dist/providers/sessionlog.d.ts +81 -0
  352. package/dist/providers/sessionlog.d.ts.map +1 -0
  353. package/dist/providers/sessionlog.js +478 -0
  354. package/dist/providers/sessionlog.js.map +1 -0
  355. package/dist/providers/sudocode.d.ts.map +1 -1
  356. package/dist/providers/sudocode.js +17 -1
  357. package/dist/providers/sudocode.js.map +1 -1
  358. package/dist/providers/traits/Reconcilable.d.ts +57 -0
  359. package/dist/providers/traits/Reconcilable.d.ts.map +1 -0
  360. package/dist/providers/traits/Reconcilable.js +18 -0
  361. package/dist/providers/traits/Reconcilable.js.map +1 -0
  362. package/dist/providers/traits/__tests__/RelationshipQueryable.test.d.ts +2 -0
  363. package/dist/providers/traits/__tests__/RelationshipQueryable.test.d.ts.map +1 -0
  364. package/dist/providers/traits/__tests__/RelationshipQueryable.test.js +169 -0
  365. package/dist/providers/traits/__tests__/RelationshipQueryable.test.js.map +1 -0
  366. package/dist/providers/traits/__tests__/TaskManageable.test.d.ts +2 -0
  367. package/dist/providers/traits/__tests__/TaskManageable.test.d.ts.map +1 -0
  368. package/dist/providers/traits/__tests__/TaskManageable.test.js +172 -0
  369. package/dist/providers/traits/__tests__/TaskManageable.test.js.map +1 -0
  370. package/dist/providers/traits/index.d.ts +2 -0
  371. package/dist/providers/traits/index.d.ts.map +1 -1
  372. package/dist/providers/traits/index.js +1 -0
  373. package/dist/providers/traits/index.js.map +1 -1
  374. package/dist/providers/types.d.ts +63 -0
  375. package/dist/providers/types.d.ts.map +1 -1
  376. package/dist/providers/types.js +22 -0
  377. package/dist/providers/types.js.map +1 -1
  378. package/dist/schema/__tests__/validation.test.d.ts +2 -0
  379. package/dist/schema/__tests__/validation.test.d.ts.map +1 -0
  380. package/dist/schema/__tests__/validation.test.js +241 -0
  381. package/dist/schema/__tests__/validation.test.js.map +1 -0
  382. package/dist/schema/edges.d.ts +3 -3
  383. package/dist/schema/edges.d.ts.map +1 -1
  384. package/dist/sessionlog/agent/agents/claude-code.d.ts +76 -0
  385. package/dist/sessionlog/agent/agents/claude-code.d.ts.map +1 -0
  386. package/dist/sessionlog/agent/agents/claude-code.js +759 -0
  387. package/dist/sessionlog/agent/agents/claude-code.js.map +1 -0
  388. package/dist/sessionlog/agent/agents/cursor.d.ts +35 -0
  389. package/dist/sessionlog/agent/agents/cursor.d.ts.map +1 -0
  390. package/dist/sessionlog/agent/agents/cursor.js +294 -0
  391. package/dist/sessionlog/agent/agents/cursor.js.map +1 -0
  392. package/dist/sessionlog/agent/agents/gemini-cli.d.ts +62 -0
  393. package/dist/sessionlog/agent/agents/gemini-cli.d.ts.map +1 -0
  394. package/dist/sessionlog/agent/agents/gemini-cli.js +462 -0
  395. package/dist/sessionlog/agent/agents/gemini-cli.js.map +1 -0
  396. package/dist/sessionlog/agent/agents/opencode.d.ts +100 -0
  397. package/dist/sessionlog/agent/agents/opencode.d.ts.map +1 -0
  398. package/dist/sessionlog/agent/agents/opencode.js +423 -0
  399. package/dist/sessionlog/agent/agents/opencode.js.map +1 -0
  400. package/dist/sessionlog/agent/registry.d.ts +54 -0
  401. package/dist/sessionlog/agent/registry.d.ts.map +1 -0
  402. package/dist/sessionlog/agent/registry.js +123 -0
  403. package/dist/sessionlog/agent/registry.js.map +1 -0
  404. package/dist/sessionlog/agent/session-types.d.ts +45 -0
  405. package/dist/sessionlog/agent/session-types.d.ts.map +1 -0
  406. package/dist/sessionlog/agent/session-types.js +50 -0
  407. package/dist/sessionlog/agent/session-types.js.map +1 -0
  408. package/dist/sessionlog/agent/types.d.ts +126 -0
  409. package/dist/sessionlog/agent/types.d.ts.map +1 -0
  410. package/dist/sessionlog/agent/types.js +39 -0
  411. package/dist/sessionlog/agent/types.js.map +1 -0
  412. package/dist/sessionlog/commands/clean.d.ts +40 -0
  413. package/dist/sessionlog/commands/clean.d.ts.map +1 -0
  414. package/dist/sessionlog/commands/clean.js +105 -0
  415. package/dist/sessionlog/commands/clean.js.map +1 -0
  416. package/dist/sessionlog/commands/disable.d.ts +23 -0
  417. package/dist/sessionlog/commands/disable.d.ts.map +1 -0
  418. package/dist/sessionlog/commands/disable.js +57 -0
  419. package/dist/sessionlog/commands/disable.js.map +1 -0
  420. package/dist/sessionlog/commands/doctor.d.ts +43 -0
  421. package/dist/sessionlog/commands/doctor.d.ts.map +1 -0
  422. package/dist/sessionlog/commands/doctor.js +97 -0
  423. package/dist/sessionlog/commands/doctor.js.map +1 -0
  424. package/dist/sessionlog/commands/enable.d.ts +31 -0
  425. package/dist/sessionlog/commands/enable.d.ts.map +1 -0
  426. package/dist/sessionlog/commands/enable.js +102 -0
  427. package/dist/sessionlog/commands/enable.js.map +1 -0
  428. package/dist/sessionlog/commands/explain.d.ts +69 -0
  429. package/dist/sessionlog/commands/explain.d.ts.map +1 -0
  430. package/dist/sessionlog/commands/explain.js +185 -0
  431. package/dist/sessionlog/commands/explain.js.map +1 -0
  432. package/dist/sessionlog/commands/reset.d.ts +23 -0
  433. package/dist/sessionlog/commands/reset.d.ts.map +1 -0
  434. package/dist/sessionlog/commands/reset.js +68 -0
  435. package/dist/sessionlog/commands/reset.js.map +1 -0
  436. package/dist/sessionlog/commands/resume.d.ts +42 -0
  437. package/dist/sessionlog/commands/resume.d.ts.map +1 -0
  438. package/dist/sessionlog/commands/resume.js +134 -0
  439. package/dist/sessionlog/commands/resume.js.map +1 -0
  440. package/dist/sessionlog/commands/rewind.d.ts +40 -0
  441. package/dist/sessionlog/commands/rewind.d.ts.map +1 -0
  442. package/dist/sessionlog/commands/rewind.js +155 -0
  443. package/dist/sessionlog/commands/rewind.js.map +1 -0
  444. package/dist/sessionlog/commands/status.d.ts +54 -0
  445. package/dist/sessionlog/commands/status.d.ts.map +1 -0
  446. package/dist/sessionlog/commands/status.js +95 -0
  447. package/dist/sessionlog/commands/status.js.map +1 -0
  448. package/dist/sessionlog/config.d.ts +40 -0
  449. package/dist/sessionlog/config.d.ts.map +1 -0
  450. package/dist/sessionlog/config.js +126 -0
  451. package/dist/sessionlog/config.js.map +1 -0
  452. package/dist/sessionlog/git-operations.d.ts +173 -0
  453. package/dist/sessionlog/git-operations.d.ts.map +1 -0
  454. package/dist/sessionlog/git-operations.js +399 -0
  455. package/dist/sessionlog/git-operations.js.map +1 -0
  456. package/dist/sessionlog/hooks/git-hooks.d.ts +22 -0
  457. package/dist/sessionlog/hooks/git-hooks.d.ts.map +1 -0
  458. package/dist/sessionlog/hooks/git-hooks.js +145 -0
  459. package/dist/sessionlog/hooks/git-hooks.js.map +1 -0
  460. package/dist/sessionlog/hooks/lifecycle.d.ts +21 -0
  461. package/dist/sessionlog/hooks/lifecycle.d.ts.map +1 -0
  462. package/dist/sessionlog/hooks/lifecycle.js +179 -0
  463. package/dist/sessionlog/hooks/lifecycle.js.map +1 -0
  464. package/dist/sessionlog/index.d.ts +69 -0
  465. package/dist/sessionlog/index.d.ts.map +1 -0
  466. package/dist/sessionlog/index.js +154 -0
  467. package/dist/sessionlog/index.js.map +1 -0
  468. package/dist/sessionlog/security/redaction.d.ts +35 -0
  469. package/dist/sessionlog/security/redaction.d.ts.map +1 -0
  470. package/dist/sessionlog/security/redaction.js +221 -0
  471. package/dist/sessionlog/security/redaction.js.map +1 -0
  472. package/dist/sessionlog/session/state-machine.d.ts +90 -0
  473. package/dist/sessionlog/session/state-machine.d.ts.map +1 -0
  474. package/dist/sessionlog/session/state-machine.js +347 -0
  475. package/dist/sessionlog/session/state-machine.js.map +1 -0
  476. package/dist/sessionlog/store/checkpoint-store.d.ts +47 -0
  477. package/dist/sessionlog/store/checkpoint-store.d.ts.map +1 -0
  478. package/dist/sessionlog/store/checkpoint-store.js +309 -0
  479. package/dist/sessionlog/store/checkpoint-store.js.map +1 -0
  480. package/dist/sessionlog/store/native-store.d.ts +21 -0
  481. package/dist/sessionlog/store/native-store.d.ts.map +1 -0
  482. package/dist/sessionlog/store/native-store.js +162 -0
  483. package/dist/sessionlog/store/native-store.js.map +1 -0
  484. package/dist/sessionlog/store/provider-types.d.ts +78 -0
  485. package/dist/sessionlog/store/provider-types.d.ts.map +1 -0
  486. package/dist/sessionlog/store/provider-types.js +12 -0
  487. package/dist/sessionlog/store/provider-types.js.map +1 -0
  488. package/dist/sessionlog/store/session-store.d.ts +28 -0
  489. package/dist/sessionlog/store/session-store.d.ts.map +1 -0
  490. package/dist/sessionlog/store/session-store.js +187 -0
  491. package/dist/sessionlog/store/session-store.js.map +1 -0
  492. package/dist/sessionlog/strategy/attribution.d.ts +39 -0
  493. package/dist/sessionlog/strategy/attribution.d.ts.map +1 -0
  494. package/dist/sessionlog/strategy/attribution.js +227 -0
  495. package/dist/sessionlog/strategy/attribution.js.map +1 -0
  496. package/dist/sessionlog/strategy/common.d.ts +60 -0
  497. package/dist/sessionlog/strategy/common.d.ts.map +1 -0
  498. package/dist/sessionlog/strategy/common.js +162 -0
  499. package/dist/sessionlog/strategy/common.js.map +1 -0
  500. package/dist/sessionlog/strategy/content-overlap.d.ts +33 -0
  501. package/dist/sessionlog/strategy/content-overlap.d.ts.map +1 -0
  502. package/dist/sessionlog/strategy/content-overlap.js +168 -0
  503. package/dist/sessionlog/strategy/content-overlap.js.map +1 -0
  504. package/dist/sessionlog/strategy/manual-commit.d.ts +35 -0
  505. package/dist/sessionlog/strategy/manual-commit.d.ts.map +1 -0
  506. package/dist/sessionlog/strategy/manual-commit.js +732 -0
  507. package/dist/sessionlog/strategy/manual-commit.js.map +1 -0
  508. package/dist/sessionlog/strategy/types.d.ts +163 -0
  509. package/dist/sessionlog/strategy/types.d.ts.map +1 -0
  510. package/dist/sessionlog/strategy/types.js +49 -0
  511. package/dist/sessionlog/strategy/types.js.map +1 -0
  512. package/dist/sessionlog/summarize/claude-generator.d.ts +25 -0
  513. package/dist/sessionlog/summarize/claude-generator.d.ts.map +1 -0
  514. package/dist/sessionlog/summarize/claude-generator.js +87 -0
  515. package/dist/sessionlog/summarize/claude-generator.js.map +1 -0
  516. package/dist/sessionlog/summarize/summarize.d.ts +52 -0
  517. package/dist/sessionlog/summarize/summarize.d.ts.map +1 -0
  518. package/dist/sessionlog/summarize/summarize.js +335 -0
  519. package/dist/sessionlog/summarize/summarize.js.map +1 -0
  520. package/dist/sessionlog/types.d.ts +298 -0
  521. package/dist/sessionlog/types.d.ts.map +1 -0
  522. package/dist/sessionlog/types.js +104 -0
  523. package/dist/sessionlog/types.js.map +1 -0
  524. package/dist/sessionlog/utils/chunk-files.d.ts +25 -0
  525. package/dist/sessionlog/utils/chunk-files.d.ts.map +1 -0
  526. package/dist/sessionlog/utils/chunk-files.js +47 -0
  527. package/dist/sessionlog/utils/chunk-files.js.map +1 -0
  528. package/dist/sessionlog/utils/commit-message.d.ts +11 -0
  529. package/dist/sessionlog/utils/commit-message.d.ts.map +1 -0
  530. package/dist/sessionlog/utils/commit-message.js +54 -0
  531. package/dist/sessionlog/utils/commit-message.js.map +1 -0
  532. package/dist/sessionlog/utils/detect-agent.d.ts +19 -0
  533. package/dist/sessionlog/utils/detect-agent.d.ts.map +1 -0
  534. package/dist/sessionlog/utils/detect-agent.js +34 -0
  535. package/dist/sessionlog/utils/detect-agent.js.map +1 -0
  536. package/dist/sessionlog/utils/hook-managers.d.ts +24 -0
  537. package/dist/sessionlog/utils/hook-managers.d.ts.map +1 -0
  538. package/dist/sessionlog/utils/hook-managers.js +87 -0
  539. package/dist/sessionlog/utils/hook-managers.js.map +1 -0
  540. package/dist/sessionlog/utils/ide-tags.d.ts +12 -0
  541. package/dist/sessionlog/utils/ide-tags.d.ts.map +1 -0
  542. package/dist/sessionlog/utils/ide-tags.js +30 -0
  543. package/dist/sessionlog/utils/ide-tags.js.map +1 -0
  544. package/dist/sessionlog/utils/paths.d.ts +32 -0
  545. package/dist/sessionlog/utils/paths.d.ts.map +1 -0
  546. package/dist/sessionlog/utils/paths.js +55 -0
  547. package/dist/sessionlog/utils/paths.js.map +1 -0
  548. package/dist/sessionlog/utils/preview-rewind.d.ts +23 -0
  549. package/dist/sessionlog/utils/preview-rewind.d.ts.map +1 -0
  550. package/dist/sessionlog/utils/preview-rewind.js +63 -0
  551. package/dist/sessionlog/utils/preview-rewind.js.map +1 -0
  552. package/dist/sessionlog/utils/rewind-conflict.d.ts +52 -0
  553. package/dist/sessionlog/utils/rewind-conflict.d.ts.map +1 -0
  554. package/dist/sessionlog/utils/rewind-conflict.js +79 -0
  555. package/dist/sessionlog/utils/rewind-conflict.js.map +1 -0
  556. package/dist/sessionlog/utils/shadow-branch.d.ts +53 -0
  557. package/dist/sessionlog/utils/shadow-branch.d.ts.map +1 -0
  558. package/dist/sessionlog/utils/shadow-branch.js +97 -0
  559. package/dist/sessionlog/utils/shadow-branch.js.map +1 -0
  560. package/dist/sessionlog/utils/string-utils.d.ts +24 -0
  561. package/dist/sessionlog/utils/string-utils.d.ts.map +1 -0
  562. package/dist/sessionlog/utils/string-utils.js +47 -0
  563. package/dist/sessionlog/utils/string-utils.js.map +1 -0
  564. package/dist/sessionlog/utils/todo-extract.d.ts +52 -0
  565. package/dist/sessionlog/utils/todo-extract.d.ts.map +1 -0
  566. package/dist/sessionlog/utils/todo-extract.js +167 -0
  567. package/dist/sessionlog/utils/todo-extract.js.map +1 -0
  568. package/dist/sessionlog/utils/trailers.d.ts +36 -0
  569. package/dist/sessionlog/utils/trailers.d.ts.map +1 -0
  570. package/dist/sessionlog/utils/trailers.js +149 -0
  571. package/dist/sessionlog/utils/trailers.js.map +1 -0
  572. package/dist/sessionlog/utils/transcript-parse.d.ts +57 -0
  573. package/dist/sessionlog/utils/transcript-parse.d.ts.map +1 -0
  574. package/dist/sessionlog/utils/transcript-parse.js +126 -0
  575. package/dist/sessionlog/utils/transcript-parse.js.map +1 -0
  576. package/dist/sessionlog/utils/transcript-timestamp.d.ts +22 -0
  577. package/dist/sessionlog/utils/transcript-timestamp.d.ts.map +1 -0
  578. package/dist/sessionlog/utils/transcript-timestamp.js +56 -0
  579. package/dist/sessionlog/utils/transcript-timestamp.js.map +1 -0
  580. package/dist/sessionlog/utils/tree-ops.d.ts +47 -0
  581. package/dist/sessionlog/utils/tree-ops.d.ts.map +1 -0
  582. package/dist/sessionlog/utils/tree-ops.js +145 -0
  583. package/dist/sessionlog/utils/tree-ops.js.map +1 -0
  584. package/dist/sessionlog/utils/tty.d.ts +25 -0
  585. package/dist/sessionlog/utils/tty.d.ts.map +1 -0
  586. package/dist/sessionlog/utils/tty.js +70 -0
  587. package/dist/sessionlog/utils/tty.js.map +1 -0
  588. package/dist/sessionlog/utils/validation.d.ts +31 -0
  589. package/dist/sessionlog/utils/validation.d.ts.map +1 -0
  590. package/dist/sessionlog/utils/validation.js +59 -0
  591. package/dist/sessionlog/utils/validation.js.map +1 -0
  592. package/dist/sessionlog/utils/worktree.d.ts +16 -0
  593. package/dist/sessionlog/utils/worktree.d.ts.map +1 -0
  594. package/dist/sessionlog/utils/worktree.js +50 -0
  595. package/dist/sessionlog/utils/worktree.js.map +1 -0
  596. package/dist/storage/__tests__/atomic-write.test.d.ts +5 -0
  597. package/dist/storage/__tests__/atomic-write.test.d.ts.map +1 -0
  598. package/dist/storage/__tests__/atomic-write.test.js +170 -0
  599. package/dist/storage/__tests__/atomic-write.test.js.map +1 -0
  600. package/dist/storage/__tests__/file-lock.test.d.ts +2 -0
  601. package/dist/storage/__tests__/file-lock.test.d.ts.map +1 -0
  602. package/dist/storage/__tests__/file-lock.test.js +89 -0
  603. package/dist/storage/__tests__/file-lock.test.js.map +1 -0
  604. package/dist/storage/__tests__/jsonl.test.d.ts +2 -0
  605. package/dist/storage/__tests__/jsonl.test.d.ts.map +1 -0
  606. package/dist/storage/__tests__/jsonl.test.js +228 -0
  607. package/dist/storage/__tests__/jsonl.test.js.map +1 -0
  608. package/dist/storage/__tests__/locked-writer.test.d.ts +2 -0
  609. package/dist/storage/__tests__/locked-writer.test.d.ts.map +1 -0
  610. package/dist/storage/__tests__/locked-writer.test.js +109 -0
  611. package/dist/storage/__tests__/locked-writer.test.js.map +1 -0
  612. package/dist/storage/__tests__/sqlite.test.d.ts +2 -0
  613. package/dist/storage/__tests__/sqlite.test.d.ts.map +1 -0
  614. package/dist/storage/__tests__/sqlite.test.js +470 -0
  615. package/dist/storage/__tests__/sqlite.test.js.map +1 -0
  616. package/dist/storage/sqlite-schema.d.ts.map +1 -1
  617. package/dist/storage/sqlite-schema.js +2 -0
  618. package/dist/storage/sqlite-schema.js.map +1 -1
  619. package/dist/storage/sqlite.d.ts.map +1 -1
  620. package/dist/storage/sqlite.js +18 -4
  621. package/dist/storage/sqlite.js.map +1 -1
  622. package/dist/tools/__tests__/annotate.test.d.ts +5 -0
  623. package/dist/tools/__tests__/annotate.test.d.ts.map +1 -0
  624. package/dist/tools/__tests__/annotate.test.js +314 -0
  625. package/dist/tools/__tests__/annotate.test.js.map +1 -0
  626. package/dist/tools/__tests__/link.test.d.ts +5 -0
  627. package/dist/tools/__tests__/link.test.d.ts.map +1 -0
  628. package/dist/tools/__tests__/link.test.js +245 -0
  629. package/dist/tools/__tests__/link.test.js.map +1 -0
  630. package/dist/tools/__tests__/query.test.d.ts +5 -0
  631. package/dist/tools/__tests__/query.test.d.ts.map +1 -0
  632. package/dist/tools/__tests__/query.test.js +288 -0
  633. package/dist/tools/__tests__/query.test.js.map +1 -0
  634. package/dist/tools/__tests__/task.test.d.ts +5 -0
  635. package/dist/tools/__tests__/task.test.d.ts.map +1 -0
  636. package/dist/tools/__tests__/task.test.js +178 -0
  637. package/dist/tools/__tests__/task.test.js.map +1 -0
  638. package/dist/tools/index.d.ts +1 -1
  639. package/dist/tools/index.d.ts.map +1 -1
  640. package/dist/tools/index.js.map +1 -1
  641. package/dist/tools/query.d.ts +2 -1
  642. package/dist/tools/query.d.ts.map +1 -1
  643. package/dist/tools/query.js +217 -7
  644. package/dist/tools/query.js.map +1 -1
  645. package/dist/tools/types.d.ts +57 -0
  646. package/dist/tools/types.d.ts.map +1 -1
  647. package/dist/tools/types.js.map +1 -1
  648. package/dist/tracking/claude-task-reconstructor.d.ts +41 -0
  649. package/dist/tracking/claude-task-reconstructor.d.ts.map +1 -0
  650. package/dist/tracking/claude-task-reconstructor.js +91 -0
  651. package/dist/tracking/claude-task-reconstructor.js.map +1 -0
  652. package/dist/tracking/plan-mode-tracker.d.ts +20 -0
  653. package/dist/tracking/plan-mode-tracker.d.ts.map +1 -0
  654. package/dist/tracking/plan-mode-tracker.js +35 -0
  655. package/dist/tracking/plan-mode-tracker.js.map +1 -0
  656. package/dist/tracking/transcript-extractor.d.ts +3 -3
  657. package/dist/tracking/transcript-extractor.d.ts.map +1 -1
  658. package/dist/tracking/transcript-extractor.js +1 -1
  659. package/dist/tracking/transcript-extractor.js.map +1 -1
  660. package/package.json +3 -1
@@ -11,6 +11,7 @@ import { createNativeProvider } from '../providers/native.js';
11
11
  import { createMaterializationManager } from '../providers/materialization.js';
12
12
  import { isWatchable, } from '../providers/traits/Watchable.js';
13
13
  import { isTaskManageable, } from '../providers/traits/TaskManageable.js';
14
+ import { isReconcilable } from '../providers/traits/Reconcilable.js';
14
15
  // ============================================================================
15
16
  // Constants
16
17
  // ============================================================================
@@ -27,6 +28,23 @@ const LOCAL_ID_PATTERN = /^[ctfex]-[a-z0-9]+$/;
27
28
  function isLocalId(idOrUri) {
28
29
  return LOCAL_ID_PATTERN.test(idOrUri);
29
30
  }
31
+ /**
32
+ * Check if a node is backed by an external provider (either type:'external'
33
+ * or a local-provider node with metadata.provider_uri).
34
+ */
35
+ function isProviderBacked(node) {
36
+ return node.type === 'external' || typeof node.metadata?.provider_uri === 'string';
37
+ }
38
+ /**
39
+ * Get the provider URI for a node. For type:'external' nodes, returns the
40
+ * top-level uri field. For local-provider nodes, returns metadata.provider_uri.
41
+ * Returns null if the node has no provider binding.
42
+ */
43
+ function getProviderUri(node) {
44
+ if (node.type === 'external')
45
+ return node.uri;
46
+ return node.metadata?.provider_uri ?? null;
47
+ }
30
48
  /**
31
49
  * Convert CreateNodeInput to ProviderCreateInput.
32
50
  * Passes through tags and parent_id via metadata so providers can use them.
@@ -62,19 +80,94 @@ function toProviderUpdateInput(updates) {
62
80
  };
63
81
  }
64
82
  /**
65
- * Find external node by URI in the store
83
+ * Find a materialized node by provider URI.
84
+ * Searches both type:'external' nodes (by uri field) and local-provider nodes
85
+ * (by metadata.provider_uri).
66
86
  */
67
87
  async function findExternalNodeByUri(uri, store) {
68
- const nodes = await store.query.nodes({
69
- type: 'external',
70
- });
71
- for (const node of nodes) {
88
+ // First check type:'external' nodes
89
+ const externalNodes = await store.query.nodes({ type: 'external' });
90
+ for (const node of externalNodes) {
72
91
  if (node.type === 'external' && node.uri === uri) {
73
92
  return node;
74
93
  }
75
94
  }
95
+ // Then check local-provider nodes via metadata.provider_uri
96
+ // Note: search only checks title/content, not metadata. Scan all task/context
97
+ // nodes and check metadata.provider_uri. Acceptable for local graph sizes.
98
+ const taskNodes = await store.query.nodes({ type: 'task' });
99
+ for (const node of taskNodes) {
100
+ if (node.metadata?.provider_uri === uri) {
101
+ return node;
102
+ }
103
+ }
104
+ const contextNodes = await store.query.nodes({ type: 'context' });
105
+ for (const node of contextNodes) {
106
+ if (node.metadata?.provider_uri === uri) {
107
+ return node;
108
+ }
109
+ }
76
110
  return null;
77
111
  }
112
+ /**
113
+ * Extract edge information from a ProviderNode's rawData.
114
+ * Provider-specific: beads uses blocks/blockedBy/dependencies,
115
+ * sudocode uses relationships, claude-tasks uses blocks/blockedBy.
116
+ * Returns normalized edge descriptors with URIs.
117
+ */
118
+ function extractEdgesFromRawData(providerNode, nodeUri, provider) {
119
+ const raw = providerNode.rawData;
120
+ if (!raw)
121
+ return [];
122
+ const edges = [];
123
+ // blocks: this node blocks the listed IDs
124
+ if (Array.isArray(raw.blocks)) {
125
+ for (const id of raw.blocks) {
126
+ if (typeof id === 'string') {
127
+ edges.push({ fromUri: nodeUri, toUri: provider.buildUri(id), type: 'blocks' });
128
+ }
129
+ }
130
+ }
131
+ // blockedBy: listed IDs block this node
132
+ if (Array.isArray(raw.blockedBy)) {
133
+ for (const id of raw.blockedBy) {
134
+ if (typeof id === 'string') {
135
+ edges.push({ fromUri: provider.buildUri(id), toUri: nodeUri, type: 'blocks' });
136
+ }
137
+ }
138
+ }
139
+ // dependencies: structured format (beads v0.49+)
140
+ if (Array.isArray(raw.dependencies)) {
141
+ for (const dep of raw.dependencies) {
142
+ const d = dep;
143
+ if (typeof d.depends_on_id === 'string' && typeof d.issue_id === 'string') {
144
+ edges.push({
145
+ fromUri: provider.buildUri(d.depends_on_id),
146
+ toUri: provider.buildUri(d.issue_id),
147
+ type: 'blocks',
148
+ });
149
+ }
150
+ }
151
+ }
152
+ // relationships: sudocode format
153
+ if (Array.isArray(raw.relationships)) {
154
+ for (const rel of raw.relationships) {
155
+ const r = rel;
156
+ if (typeof r.target_id === 'string' && typeof r.type === 'string') {
157
+ edges.push({
158
+ fromUri: nodeUri,
159
+ toUri: provider.buildUri(r.target_id),
160
+ type: r.type,
161
+ });
162
+ }
163
+ }
164
+ }
165
+ // parent_id: sudocode parent relationship
166
+ if (typeof raw.parent_id === 'string') {
167
+ edges.push({ fromUri: provider.buildUri(raw.parent_id), toUri: nodeUri, type: 'parent' });
168
+ }
169
+ return edges;
170
+ }
78
171
  // ============================================================================
79
172
  // Factory Function
80
173
  // ============================================================================
@@ -100,6 +193,26 @@ export function createProviderAwareStore(baseStore, config = {}) {
100
193
  const changeHandlers = [];
101
194
  /** Track which providers are currently being watched */
102
195
  const watchedProviders = new Set();
196
+ // =========================================================================
197
+ // Pointer-Only Node Cache (session-scoped, in-memory)
198
+ // =========================================================================
199
+ /** TTL for pointer cache entries (30 seconds) */
200
+ const POINTER_CACHE_TTL_MS = 30_000;
201
+ /** In-memory cache for pointer-only node data resolved from providers */
202
+ const pointerCache = new Map();
203
+ function getFromPointerCache(nodeId) {
204
+ const entry = pointerCache.get(nodeId);
205
+ if (!entry)
206
+ return null;
207
+ if (Date.now() >= entry.expiresAt) {
208
+ pointerCache.delete(nodeId);
209
+ return null;
210
+ }
211
+ return entry.node;
212
+ }
213
+ function setInPointerCache(nodeId, node) {
214
+ pointerCache.set(nodeId, { node, expiresAt: Date.now() + POINTER_CACHE_TTL_MS });
215
+ }
103
216
  /**
104
217
  * Handle an inbound provider change event.
105
218
  * Auto-refreshes materialized nodes, then notifies external handlers.
@@ -108,9 +221,9 @@ export function createProviderAwareStore(baseStore, config = {}) {
108
221
  if (event.kind === 'node') {
109
222
  await handleNodeChange(providerName, event.event);
110
223
  }
111
- // Edge events: no auto-action needed for now — consumers can
112
- // react via onProviderChange handlers. In the future, the
113
- // federated graph could invalidate cached edge data here.
224
+ else if (event.kind === 'edge') {
225
+ await handleEdgeChange(providerName, event.event);
226
+ }
114
227
  // Notify external handlers
115
228
  for (const handler of changeHandlers) {
116
229
  try {
@@ -146,15 +259,72 @@ export function createProviderAwareStore(baseStore, config = {}) {
146
259
  return;
147
260
  }
148
261
  // For 'created' (re-appeared) or 'updated': refresh materialized copy
262
+ const changeProvider = registry.get(_providerName) ?? registry.resolveProvider(event.uri);
149
263
  if (event.node) {
150
264
  // Provider included the full node data — materialize directly
151
- await materialization.materialize(event.uri, event.node, baseStore);
265
+ await materialization.materialize(event.uri, event.node, baseStore, {
266
+ local: changeProvider?.local, materializeMode: changeProvider?.materializeMode,
267
+ });
152
268
  }
153
269
  else {
154
270
  // No node data in event — fetch fresh from provider
155
- const provider = registry.resolveProvider(event.uri);
156
- if (provider) {
157
- await materialization.refresh(existing, provider, baseStore);
271
+ if (changeProvider) {
272
+ if (existing.type === 'external') {
273
+ await materialization.refresh(existing, changeProvider, baseStore);
274
+ }
275
+ else {
276
+ // Local-provider node — fetch and re-materialize
277
+ const parsed = changeProvider.parseUri(event.uri);
278
+ if (parsed) {
279
+ const fresh = await changeProvider.get(parsed.id);
280
+ if (fresh) {
281
+ await materialization.materialize(event.uri, fresh, baseStore, {
282
+ local: changeProvider.local, materializeMode: changeProvider.materializeMode,
283
+ });
284
+ }
285
+ }
286
+ }
287
+ }
288
+ }
289
+ }
290
+ /**
291
+ * Handle an edge change from a provider watch event.
292
+ * Creates or deletes edges in the graph, stamped with edge_source.
293
+ */
294
+ async function handleEdgeChange(providerName, event) {
295
+ // Resolve source and target URIs to local node IDs
296
+ const fromNode = await findExternalNodeByUri(event.sourceUri, baseStore);
297
+ const toNode = await findExternalNodeByUri(event.targetUri, baseStore);
298
+ if (!fromNode || !toNode) {
299
+ // One or both nodes not materialized — skip edge
300
+ return;
301
+ }
302
+ if (event.type === 'created') {
303
+ // Check if edge already exists
304
+ const existingEdges = await baseStore.query.edgesFrom(fromNode.id, event.edge.type);
305
+ const alreadyExists = existingEdges.some((e) => e.to_id === toNode.id && e.source === providerName);
306
+ if (!alreadyExists) {
307
+ await baseStore.createEdge({
308
+ from_id: fromNode.id,
309
+ to_id: toNode.id,
310
+ type: event.edge.type,
311
+ metadata: {
312
+ edge_source: providerName,
313
+ from_uri: event.sourceUri,
314
+ to_uri: event.targetUri,
315
+ ...event.edge.metadata,
316
+ },
317
+ });
318
+ }
319
+ }
320
+ else if (event.type === 'deleted') {
321
+ // Find and delete the provider-owned edge
322
+ const existingEdges = await baseStore.query.edgesFrom(fromNode.id, event.edge.type);
323
+ for (const edge of existingEdges) {
324
+ if (edge.to_id === toNode.id &&
325
+ (edge.source === providerName || edge.metadata?.edge_source === providerName)) {
326
+ await baseStore.deleteEdge(edge.id);
327
+ }
158
328
  }
159
329
  }
160
330
  }
@@ -190,7 +360,9 @@ export function createProviderAwareStore(baseStore, config = {}) {
190
360
  throw new Error(`Node not found: ${uri}`);
191
361
  }
192
362
  // Materialize
193
- return materialization.materialize(uri, providerNode, baseStore);
363
+ return materialization.materialize(uri, providerNode, baseStore, {
364
+ local: provider.local, materializeMode: provider.materializeMode,
365
+ });
194
366
  },
195
367
  /**
196
368
  * Refresh a materialized external node
@@ -289,7 +461,9 @@ export function createProviderAwareStore(baseStore, config = {}) {
289
461
  const providerNode = await provider.create(toProviderCreateInput(input), options?.context);
290
462
  // Build canonical URI and always materialize on create
291
463
  const uri = provider.buildUri(providerNode.id);
292
- const materializedNode = await materialization.materialize(uri, providerNode, baseStore);
464
+ const materializedNode = await materialization.materialize(uri, providerNode, baseStore, {
465
+ local: provider.local, materializeMode: provider.materializeMode,
466
+ });
293
467
  return {
294
468
  node: materializedNode,
295
469
  uri,
@@ -324,23 +498,24 @@ export function createProviderAwareStore(baseStore, config = {}) {
324
498
  // Local node — update directly
325
499
  return baseStore.updateNode(node.id, updates);
326
500
  }
327
- // External node but provider not registered — error rather than silent local-only update
501
+ // Provider-backed node but provider not registered — error rather than silent local-only update
502
+ const nodeProviderUri = getProviderUri(node);
328
503
  if (!owningProvider) {
329
- const extNode = node;
330
- throw new ProviderError('NOT_FOUND', `Provider not registered for external node URI: ${extNode.uri}. Cannot route update.`);
504
+ throw new ProviderError('NOT_FOUND', `Provider not registered for node URI: ${nodeProviderUri}. Cannot route update.`);
331
505
  }
332
506
  if (!owningProvider.capabilities.write) {
333
507
  throw new ProviderError('NOT_SUPPORTED', `Provider ${owningProvider.name} is read-only`, owningProvider.name);
334
508
  }
335
- // Route update to external provider
336
- const extNode = node;
337
- const parsed = owningProvider.parseUri(extNode.uri);
509
+ // Route update to owning provider
510
+ const parsed = owningProvider.parseUri(nodeProviderUri);
338
511
  if (!parsed) {
339
- throw new ProviderError('INVALID_URI', `Cannot parse URI: ${extNode.uri}`, owningProvider.name);
512
+ throw new ProviderError('INVALID_URI', `Cannot parse URI: ${nodeProviderUri}`, owningProvider.name);
340
513
  }
341
514
  const updatedProviderNode = await owningProvider.update(parsed.id, toProviderUpdateInput(updates), options?.context);
342
515
  // Refresh local materialized copy with provider's response
343
- return materialization.materialize(extNode.uri, updatedProviderNode, baseStore);
516
+ return materialization.materialize(nodeProviderUri, updatedProviderNode, baseStore, {
517
+ local: owningProvider.local, materializeMode: owningProvider.materializeMode,
518
+ });
344
519
  },
345
520
  async providerDelete(idOrUri, options) {
346
521
  // Resolve the target node and provider
@@ -350,19 +525,18 @@ export function createProviderAwareStore(baseStore, config = {}) {
350
525
  await baseStore.deleteNode(node.id, options);
351
526
  return;
352
527
  }
353
- // External node but provider not registered — error rather than silent local-only delete
528
+ // Provider-backed node but provider not registered — error rather than silent local-only delete
529
+ const deleteProviderUri = getProviderUri(node);
354
530
  if (!owningProvider) {
355
- const extNode = node;
356
- throw new ProviderError('NOT_FOUND', `Provider not registered for external node URI: ${extNode.uri}. Cannot route delete.`);
531
+ throw new ProviderError('NOT_FOUND', `Provider not registered for node URI: ${deleteProviderUri}. Cannot route delete.`);
357
532
  }
358
533
  if (!owningProvider.capabilities.write) {
359
534
  throw new ProviderError('NOT_SUPPORTED', `Provider ${owningProvider.name} is read-only`, owningProvider.name);
360
535
  }
361
- // Delete in external provider
362
- const extNode = node;
363
- const parsed = owningProvider.parseUri(extNode.uri);
536
+ // Delete in owning provider
537
+ const parsed = owningProvider.parseUri(deleteProviderUri);
364
538
  if (!parsed) {
365
- throw new ProviderError('INVALID_URI', `Cannot parse URI: ${extNode.uri}`, owningProvider.name);
539
+ throw new ProviderError('INVALID_URI', `Cannot parse URI: ${deleteProviderUri}`, owningProvider.name);
366
540
  }
367
541
  await owningProvider.delete(parsed.id, options?.context);
368
542
  // Remove local materialized copy
@@ -382,17 +556,97 @@ export function createProviderAwareStore(baseStore, config = {}) {
382
556
  }
383
557
  return { node, provider: provider.name, action };
384
558
  }
385
- // Materialize the result for external providers
559
+ // Materialize the result for non-native providers
386
560
  const uri = provider.buildUri(updatedNode.id);
387
- const materialized = await materialization.materialize(uri, updatedNode, baseStore);
561
+ const materialized = await materialization.materialize(uri, updatedNode, baseStore, {
562
+ local: provider.local, materializeMode: provider.materializeMode,
563
+ });
388
564
  return {
389
565
  node: materialized,
390
566
  provider: provider.name,
391
567
  action,
392
568
  };
393
569
  },
570
+ async providerListTasks(options) {
571
+ const results = [];
572
+ const seenUris = new Set();
573
+ // 1. Query native graph store for type:'task' nodes
574
+ const nativeFilter = { type: 'task' };
575
+ if (options?.status)
576
+ nativeFilter.status = options.status;
577
+ if (options?.tags)
578
+ nativeFilter.tags = options.tags;
579
+ if (options?.assignee)
580
+ nativeFilter.assignee = options.assignee;
581
+ if (options?.search)
582
+ nativeFilter.search = options.search;
583
+ const nativeNodes = await baseStore.query.nodes(nativeFilter);
584
+ const seenIds = new Set();
585
+ for (const node of nativeNodes) {
586
+ results.push(node);
587
+ seenIds.add(node.id);
588
+ // Track provider URIs from local-provider nodes to prevent duplicates
589
+ // when the provider federation loop re-discovers the same tasks
590
+ const pUri = getProviderUri(node);
591
+ if (pUri) {
592
+ seenUris.add(pUri);
593
+ }
594
+ }
595
+ // 2. Query each registered provider's list()
596
+ for (const provider of registry.list()) {
597
+ if (provider.name === 'native')
598
+ continue; // already covered above
599
+ if (options?.providers && !options.providers.includes(provider.name))
600
+ continue;
601
+ try {
602
+ const providerFilter = {};
603
+ if (options?.status) {
604
+ providerFilter.status = Array.isArray(options.status) ? options.status[0] : options.status;
605
+ }
606
+ if (options?.search)
607
+ providerFilter.search = options.search;
608
+ const providerNodes = await provider.list(providerFilter, options?.context);
609
+ for (const pNode of providerNodes) {
610
+ const uri = provider.buildUri(pNode.id);
611
+ if (seenUris.has(uri))
612
+ continue;
613
+ seenUris.add(uri);
614
+ // Check if already materialized in graph
615
+ const existing = await findExternalNodeByUri(uri, baseStore);
616
+ if (existing) {
617
+ // Avoid pushing the same graph node twice (already in results from native query)
618
+ if (!seenIds.has(existing.id)) {
619
+ results.push(existing);
620
+ seenIds.add(existing.id);
621
+ }
622
+ }
623
+ else {
624
+ // Materialize so callers get Node objects with local IDs
625
+ const materialized = await materialization.materialize(uri, pNode, baseStore, {
626
+ local: provider.local, materializeMode: provider.materializeMode,
627
+ });
628
+ results.push(materialized);
629
+ seenIds.add(materialized.id);
630
+ }
631
+ }
632
+ }
633
+ catch {
634
+ // Provider unavailable — skip gracefully
635
+ }
636
+ }
637
+ // 3. Apply limit after aggregation
638
+ if (options?.limit && results.length > options.limit) {
639
+ const offset = options?.offset ?? 0;
640
+ return results.slice(offset, offset + options.limit);
641
+ }
642
+ if (options?.offset) {
643
+ return results.slice(options.offset);
644
+ }
645
+ return results;
646
+ },
394
647
  async taskReady(options) {
395
648
  const results = [];
649
+ const seenIds = new Set();
396
650
  for (const provider of registry.list()) {
397
651
  if (!isTaskManageable(provider))
398
652
  continue;
@@ -408,22 +662,31 @@ export function createProviderAwareStore(baseStore, config = {}) {
408
662
  if (provider.name === 'native') {
409
663
  for (const pNode of readyNodes) {
410
664
  const node = await baseStore.getNode(pNode.id);
411
- if (node) {
665
+ if (node && !seenIds.has(node.id)) {
412
666
  results.push(node);
667
+ seenIds.add(node.id);
413
668
  }
414
669
  }
415
670
  continue;
416
671
  }
417
- // Materialize each ready task from external providers so callers get Node objects
672
+ // Materialize each ready task from non-native providers so callers get Node objects
418
673
  for (const pNode of readyNodes) {
419
674
  const uri = provider.buildUri(pNode.id);
420
675
  const existing = await findExternalNodeByUri(uri, baseStore);
421
676
  if (existing) {
422
- results.push(existing);
677
+ if (!seenIds.has(existing.id)) {
678
+ results.push(existing);
679
+ seenIds.add(existing.id);
680
+ }
423
681
  }
424
682
  else {
425
- const materialized = await materialization.materialize(uri, pNode, baseStore);
426
- results.push(materialized);
683
+ const materialized = await materialization.materialize(uri, pNode, baseStore, {
684
+ local: provider.local, materializeMode: provider.materializeMode,
685
+ });
686
+ if (!seenIds.has(materialized.id)) {
687
+ results.push(materialized);
688
+ seenIds.add(materialized.id);
689
+ }
427
690
  }
428
691
  }
429
692
  }
@@ -448,7 +711,9 @@ export function createProviderAwareStore(baseStore, config = {}) {
448
711
  return node;
449
712
  }
450
713
  const uri = provider.buildUri(updatedNode.id);
451
- return materialization.materialize(uri, updatedNode, baseStore);
714
+ return materialization.materialize(uri, updatedNode, baseStore, {
715
+ local: provider.local, materializeMode: provider.materializeMode,
716
+ });
452
717
  },
453
718
  async taskValidActions(idOrUri) {
454
719
  const { provider, providerId } = await resolveTaskProvider(idOrUri);
@@ -458,33 +723,251 @@ export function createProviderAwareStore(baseStore, config = {}) {
458
723
  // Fall back to the provider's declared capabilities
459
724
  return provider.taskCapabilities.actions;
460
725
  },
726
+ // ===========================================================================
727
+ // Reconciliation
728
+ // ===========================================================================
729
+ async reconcileProviders(options) {
730
+ // 1. Scan graph for all provider-authoritative nodes
731
+ const allNodes = await baseStore.query.nodes({});
732
+ const authoritativeNodes = allNodes.filter((n) => n.metadata?.provider_authoritative === true);
733
+ // 2. Filter by options
734
+ let targetNodes = authoritativeNodes;
735
+ if (options?.nodeIds) {
736
+ const idSet = new Set(options.nodeIds);
737
+ targetNodes = targetNodes.filter((n) => idSet.has(n.id));
738
+ }
739
+ if (options?.providers) {
740
+ const providerSet = new Set(options.providers);
741
+ targetNodes = targetNodes.filter((n) => typeof n.metadata?.provider_source === 'string' && providerSet.has(n.metadata.provider_source));
742
+ }
743
+ // 3. Group by provider_source
744
+ const grouped = new Map();
745
+ for (const node of targetNodes) {
746
+ const source = node.metadata?.provider_source;
747
+ const uri = getProviderUri(node);
748
+ if (!source || !uri)
749
+ continue;
750
+ if (!grouped.has(source))
751
+ grouped.set(source, []);
752
+ grouped.get(source).push({ node, providerUri: uri });
753
+ }
754
+ const results = [];
755
+ const skippedProviders = [];
756
+ // 4. For each provider group
757
+ for (const [source, nodes] of grouped) {
758
+ const provider = registry.get(source);
759
+ // 4a. Check availability
760
+ if (!provider) {
761
+ skippedProviders.push(source);
762
+ for (const { node, providerUri } of nodes) {
763
+ results.push({ nodeId: node.id, providerUri, status: 'unavailable' });
764
+ }
765
+ continue;
766
+ }
767
+ if (provider.isAvailable) {
768
+ const available = await provider.isAvailable();
769
+ if (!available) {
770
+ skippedProviders.push(source);
771
+ for (const { node, providerUri } of nodes) {
772
+ results.push({ nodeId: node.id, providerUri, status: 'unavailable' });
773
+ }
774
+ continue;
775
+ }
776
+ }
777
+ // 4b. Determine which nodes actually need a full fetch
778
+ // If the provider is Reconcilable, use batch hashes to skip unchanged nodes
779
+ let nodesToFetch = nodes;
780
+ if (isReconcilable(provider)) {
781
+ try {
782
+ const ids = nodes.map(({ providerUri }) => {
783
+ const parsed = provider.parseUri(providerUri);
784
+ return parsed?.id;
785
+ }).filter((id) => !!id);
786
+ const summaries = await provider.listForReconciliation({ ids });
787
+ const hashByUri = new Map(summaries.map((s) => [s.uri, s.contentHash]));
788
+ // Compare hashes — only fetch nodes whose hash changed or is missing
789
+ nodesToFetch = [];
790
+ for (const entry of nodes) {
791
+ const remoteHash = hashByUri.get(entry.providerUri);
792
+ const localHash = entry.node.metadata?.provider_content_hash;
793
+ if (remoteHash && localHash && remoteHash === localHash) {
794
+ // Hash matches — unchanged, just refresh timestamp
795
+ const now = new Date().toISOString();
796
+ await baseStore.updateNode(entry.node.id, {
797
+ metadata: {
798
+ ...entry.node.metadata,
799
+ provider_cached_at: now,
800
+ provider_needs_reconcile: undefined,
801
+ },
802
+ });
803
+ results.push({ nodeId: entry.node.id, providerUri: entry.providerUri, status: 'unchanged' });
804
+ }
805
+ else {
806
+ nodesToFetch.push(entry);
807
+ }
808
+ }
809
+ }
810
+ catch {
811
+ // Batch path failed — fall back to individual gets
812
+ nodesToFetch = nodes;
813
+ }
814
+ }
815
+ // 4c. Reconcile nodes that need a full fetch via individual get() calls
816
+ for (const { node, providerUri } of nodesToFetch) {
817
+ try {
818
+ const parsed = provider.parseUri(providerUri);
819
+ if (!parsed) {
820
+ results.push({
821
+ nodeId: node.id,
822
+ providerUri,
823
+ status: 'error',
824
+ error: `Failed to parse URI: ${providerUri}`,
825
+ });
826
+ continue;
827
+ }
828
+ const providerNode = await provider.get(parsed.id);
829
+ if (!providerNode) {
830
+ // Provider returns null (deleted in provider) — no action (positive-writes-only)
831
+ results.push({ nodeId: node.id, providerUri, status: 'unchanged' });
832
+ continue;
833
+ }
834
+ // Compare: did anything change?
835
+ const now = new Date().toISOString();
836
+ const titleChanged = providerNode.title !== node.title;
837
+ const contentChanged = providerNode.content !== (node.content ?? undefined);
838
+ const nodeStatus = 'status' in node ? node.status : undefined;
839
+ const nodePriority = 'priority' in node ? node.priority : undefined;
840
+ const statusChanged = providerNode.status !== nodeStatus;
841
+ const priorityChanged = providerNode.priority !== nodePriority;
842
+ if (titleChanged || contentChanged || statusChanged || priorityChanged) {
843
+ // Update graph node with provider data
844
+ const isPointer = node.metadata?.provider_pointer_only === true;
845
+ await baseStore.updateNode(node.id, {
846
+ ...(isPointer
847
+ ? {}
848
+ : {
849
+ title: providerNode.title,
850
+ content: providerNode.content,
851
+ status: providerNode.status,
852
+ priority: providerNode.priority,
853
+ }),
854
+ metadata: {
855
+ ...node.metadata,
856
+ provider_cached_at: isPointer ? undefined : now,
857
+ provider_content_hash: providerNode.contentHash ?? undefined,
858
+ provider_needs_reconcile: undefined, // clear flag
859
+ },
860
+ });
861
+ results.push({ nodeId: node.id, providerUri, status: 'updated' });
862
+ }
863
+ else {
864
+ // Same data — just stamp provider_cached_at and hash
865
+ await baseStore.updateNode(node.id, {
866
+ metadata: {
867
+ ...node.metadata,
868
+ provider_cached_at: now,
869
+ provider_content_hash: providerNode.contentHash ?? node.metadata?.provider_content_hash,
870
+ provider_needs_reconcile: undefined, // clear flag
871
+ },
872
+ });
873
+ results.push({ nodeId: node.id, providerUri, status: 'unchanged' });
874
+ }
875
+ // 4d. Edge reconciliation — extract edges from ProviderNode.rawData
876
+ // (zero extra provider calls — piggyback on node data already fetched)
877
+ if (providerNode.rawData) {
878
+ try {
879
+ const rawEdges = extractEdgesFromRawData(providerNode, providerUri, provider);
880
+ if (rawEdges.length > 0) {
881
+ // Get existing edges owned by this provider for this node
882
+ const existingEdgesFrom = await baseStore.query.edgesFrom(node.id);
883
+ const existingEdgesTo = await baseStore.query.edgesTo(node.id);
884
+ const allExisting = [...existingEdgesFrom, ...existingEdgesTo];
885
+ const providerOwnedEdges = allExisting.filter((e) => e.source === source || e.metadata?.edge_source === source);
886
+ // Build sets for diffing
887
+ const providerEdgeKeys = new Set(rawEdges.map((pe) => `${pe.fromUri}|${pe.toUri}|${pe.type}`));
888
+ const existingEdgeKeys = new Map();
889
+ for (const e of providerOwnedEdges) {
890
+ const fromUri = e.metadata?.from_uri ?? e.from_id;
891
+ const toUri = e.metadata?.to_uri ?? e.to_id;
892
+ existingEdgeKeys.set(`${fromUri}|${toUri}|${e.type}`, { id: e.id });
893
+ }
894
+ // Add edges that exist in provider but not in graph
895
+ for (const pe of rawEdges) {
896
+ const key = `${pe.fromUri}|${pe.toUri}|${pe.type}`;
897
+ if (!existingEdgeKeys.has(key)) {
898
+ const fromNode = await findExternalNodeByUri(pe.fromUri, baseStore);
899
+ const toNode = await findExternalNodeByUri(pe.toUri, baseStore);
900
+ if (fromNode && toNode) {
901
+ await baseStore.createEdge({
902
+ from_id: fromNode.id,
903
+ to_id: toNode.id,
904
+ type: pe.type,
905
+ metadata: {
906
+ edge_source: source,
907
+ from_uri: pe.fromUri,
908
+ to_uri: pe.toUri,
909
+ },
910
+ });
911
+ }
912
+ }
913
+ }
914
+ // Remove edges that exist in graph but not in provider
915
+ for (const [key, edge] of existingEdgeKeys) {
916
+ if (!providerEdgeKeys.has(key)) {
917
+ await baseStore.deleteEdge(edge.id);
918
+ }
919
+ }
920
+ }
921
+ }
922
+ catch {
923
+ // Edge reconciliation failure is non-fatal
924
+ }
925
+ }
926
+ }
927
+ catch (error) {
928
+ // Error fetching — leave node as-is, preserve cached data
929
+ results.push({
930
+ nodeId: node.id,
931
+ providerUri,
932
+ status: 'error',
933
+ error: error instanceof Error ? error.message : String(error),
934
+ });
935
+ }
936
+ }
937
+ }
938
+ return {
939
+ totalNodes: targetNodes.length,
940
+ results,
941
+ skippedProviders,
942
+ };
943
+ },
461
944
  };
462
945
  // ===========================================================================
463
946
  // Internal Helper: Resolve a task-capable provider for a given ID or URI
464
947
  // ===========================================================================
465
948
  async function resolveTaskProvider(idOrUri) {
466
- // If it's a local ID, check if it's an external node with a provider URI
949
+ // If it's a local ID, check if it's a provider-backed node
467
950
  if (isLocalId(idOrUri)) {
468
951
  const node = await baseStore.getNode(idOrUri);
469
952
  if (!node) {
470
953
  throw new ProviderError('NOT_FOUND', `Node not found: ${idOrUri}`);
471
954
  }
472
- if (node.type === 'external') {
473
- const extNode = node;
474
- const provider = registry.resolveProvider(extNode.uri);
955
+ const taskProviderUri = getProviderUri(node);
956
+ if (taskProviderUri) {
957
+ const provider = registry.resolveProvider(taskProviderUri);
475
958
  if (!provider) {
476
- throw new ProviderError('NOT_FOUND', `No provider found for URI: ${extNode.uri}`);
959
+ throw new ProviderError('NOT_FOUND', `No provider found for URI: ${taskProviderUri}`);
477
960
  }
478
961
  if (!isTaskManageable(provider)) {
479
962
  throw new ProviderError('NOT_SUPPORTED', `Provider ${provider.name} does not support task operations`, provider.name);
480
963
  }
481
- const parsed = provider.parseUri(extNode.uri);
964
+ const parsed = provider.parseUri(taskProviderUri);
482
965
  if (!parsed) {
483
- throw new ProviderError('INVALID_URI', `Cannot parse URI: ${extNode.uri}`, provider.name);
966
+ throw new ProviderError('INVALID_URI', `Cannot parse URI: ${taskProviderUri}`, provider.name);
484
967
  }
485
968
  return { provider, providerId: parsed.id };
486
969
  }
487
- // Local non-external node — check native provider
970
+ // Local non-provider node — check native provider
488
971
  const nativeProvider = registry.get('native');
489
972
  if (!nativeProvider || !isTaskManageable(nativeProvider)) {
490
973
  throw new ProviderError('NOT_SUPPORTED', 'Native provider does not support task operations');
@@ -514,13 +997,50 @@ export function createProviderAwareStore(baseStore, config = {}) {
514
997
  async function resolveNodeInternal(idOrUri, options, context) {
515
998
  // 1. Check if local ID - use existing getNode
516
999
  if (isLocalId(idOrUri)) {
517
- return baseStore.getNode(idOrUri);
1000
+ const localNode = await baseStore.getNode(idOrUri);
1001
+ if (!localNode)
1002
+ return null;
1003
+ // Transparent pointer-only resolution: fetch real data from provider
1004
+ if (localNode.metadata?.provider_pointer_only === true) {
1005
+ const providerUri = getProviderUri(localNode);
1006
+ if (providerUri) {
1007
+ // Check session cache first
1008
+ if (!options?.refresh) {
1009
+ const cached = getFromPointerCache(localNode.id);
1010
+ if (cached) {
1011
+ // Overlay provider data onto the local node shape
1012
+ return { ...localNode, title: cached.title, content: cached.content ?? localNode.content };
1013
+ }
1014
+ }
1015
+ // Fetch from provider
1016
+ const provider = registry.resolveProvider(providerUri);
1017
+ if (provider) {
1018
+ const parsed = provider.parseUri(providerUri);
1019
+ if (parsed) {
1020
+ try {
1021
+ const fresh = await provider.get(parsed.id, context);
1022
+ if (fresh) {
1023
+ setInPointerCache(localNode.id, fresh);
1024
+ return { ...localNode, title: fresh.title, content: fresh.content ?? localNode.content };
1025
+ }
1026
+ }
1027
+ catch {
1028
+ // Fall through to return stub node
1029
+ }
1030
+ }
1031
+ }
1032
+ }
1033
+ }
1034
+ return localNode;
518
1035
  }
519
1036
  // 2. Check for cached materialized node (unless refresh requested)
520
1037
  if (!options?.refresh) {
521
1038
  const existing = await findExternalNodeByUri(idOrUri, baseStore);
522
- if (existing && !materialization.isStale(existing)) {
523
- return existing;
1039
+ if (existing) {
1040
+ // Local-provider nodes (type !== 'external') are never stale
1041
+ if (existing.type !== 'external' || !materialization.isStale(existing)) {
1042
+ return existing;
1043
+ }
524
1044
  }
525
1045
  }
526
1046
  // 3. Find provider for this URI
@@ -543,7 +1063,9 @@ export function createProviderAwareStore(baseStore, config = {}) {
543
1063
  explicit: options?.materialize,
544
1064
  });
545
1065
  if (shouldMaterialize) {
546
- return materialization.materialize(idOrUri, providerNode, baseStore);
1066
+ return materialization.materialize(idOrUri, providerNode, baseStore, {
1067
+ local: provider.local, materializeMode: provider.materializeMode,
1068
+ });
547
1069
  }
548
1070
  // Return provider node directly
549
1071
  return providerNode;
@@ -572,7 +1094,9 @@ export function createProviderAwareStore(baseStore, config = {}) {
572
1094
  if (!providerNode) {
573
1095
  throw new ProviderError('NOT_FOUND', `Node not found: ${idOrUri}`, provider.name);
574
1096
  }
575
- const materialized = await materialization.materialize(idOrUri, providerNode, baseStore);
1097
+ const materialized = await materialization.materialize(idOrUri, providerNode, baseStore, {
1098
+ local: provider.local, materializeMode: provider.materializeMode,
1099
+ });
576
1100
  return { node: materialized, provider, isExternal: true };
577
1101
  }
578
1102
  // Case 2: Local ID (s-abc1, i-def2, x-ghi3)
@@ -580,13 +1104,13 @@ export function createProviderAwareStore(baseStore, config = {}) {
580
1104
  if (!node) {
581
1105
  throw new ProviderError('NOT_FOUND', `Node not found: ${idOrUri}`);
582
1106
  }
583
- // Check if it's a materialized external node
584
- if (node.type === 'external') {
585
- const extNode = node;
586
- const provider = registry.resolveProvider(extNode.uri);
1107
+ // Check if it's a provider-backed node (external OR local-provider with metadata.provider_uri)
1108
+ const providerUri = getProviderUri(node);
1109
+ if (providerUri) {
1110
+ const provider = registry.resolveProvider(providerUri);
587
1111
  return { node, provider, isExternal: true };
588
1112
  }
589
- // Pure local node
1113
+ // Pure local node (no provider binding)
590
1114
  return { node, provider: null, isExternal: false };
591
1115
  }
592
1116
  return providerStore;