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.
- package/README.md +68 -0
- package/dist/__tests__/cli-tools.test.d.ts +8 -0
- package/dist/__tests__/cli-tools.test.d.ts.map +1 -0
- package/dist/__tests__/cli-tools.test.js +546 -0
- package/dist/__tests__/cli-tools.test.js.map +1 -0
- package/dist/__tests__/cli.test.d.ts +5 -0
- package/dist/__tests__/cli.test.d.ts.map +1 -0
- package/dist/__tests__/cli.test.js +77 -0
- package/dist/__tests__/cli.test.js.map +1 -0
- package/dist/__tests__/p1-p3-gaps.test.d.ts +2 -0
- package/dist/__tests__/p1-p3-gaps.test.d.ts.map +1 -0
- package/dist/__tests__/p1-p3-gaps.test.js +463 -0
- package/dist/__tests__/p1-p3-gaps.test.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +64 -0
- package/dist/cli.js.map +1 -1
- package/dist/client/__tests__/client-crud.test.d.ts +7 -0
- package/dist/client/__tests__/client-crud.test.d.ts.map +1 -0
- package/dist/client/__tests__/client-crud.test.js +404 -0
- package/dist/client/__tests__/client-crud.test.js.map +1 -0
- package/dist/client/__tests__/client.test.d.ts +5 -0
- package/dist/client/__tests__/client.test.d.ts.map +1 -0
- package/dist/client/__tests__/client.test.js +518 -0
- package/dist/client/__tests__/client.test.js.map +1 -0
- package/dist/client/client.d.ts +47 -1
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +71 -0
- package/dist/client/client.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/config/__tests__/defaults.test.d.ts +2 -0
- package/dist/config/__tests__/defaults.test.d.ts.map +1 -0
- package/dist/config/__tests__/defaults.test.js +57 -0
- package/dist/config/__tests__/defaults.test.js.map +1 -0
- package/dist/config/__tests__/env.test.d.ts +2 -0
- package/dist/config/__tests__/env.test.d.ts.map +1 -0
- package/dist/config/__tests__/env.test.js +136 -0
- package/dist/config/__tests__/env.test.js.map +1 -0
- package/dist/config/__tests__/index.test.d.ts +2 -0
- package/dist/config/__tests__/index.test.d.ts.map +1 -0
- package/dist/config/__tests__/index.test.js +113 -0
- package/dist/config/__tests__/index.test.js.map +1 -0
- package/dist/config/__tests__/loader.test.d.ts +2 -0
- package/dist/config/__tests__/loader.test.d.ts.map +1 -0
- package/dist/config/__tests__/loader.test.js +128 -0
- package/dist/config/__tests__/loader.test.js.map +1 -0
- package/dist/config/__tests__/merge.test.d.ts +2 -0
- package/dist/config/__tests__/merge.test.d.ts.map +1 -0
- package/dist/config/__tests__/merge.test.js +79 -0
- package/dist/config/__tests__/merge.test.js.map +1 -0
- package/dist/config/__tests__/schema.test.d.ts +2 -0
- package/dist/config/__tests__/schema.test.d.ts.map +1 -0
- package/dist/config/__tests__/schema.test.js +300 -0
- package/dist/config/__tests__/schema.test.js.map +1 -0
- package/dist/config/schema.d.ts +178 -4
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +109 -7
- package/dist/config/schema.js.map +1 -1
- package/dist/context-files/context-files.d.ts +72 -0
- package/dist/context-files/context-files.d.ts.map +1 -0
- package/dist/context-files/context-files.js +145 -0
- package/dist/context-files/context-files.js.map +1 -0
- package/dist/context-files/index.d.ts +16 -0
- package/dist/context-files/index.d.ts.map +1 -0
- package/dist/context-files/index.js +14 -0
- package/dist/context-files/index.js.map +1 -0
- package/dist/context-files/resolver.d.ts +43 -0
- package/dist/context-files/resolver.d.ts.map +1 -0
- package/dist/context-files/resolver.js +127 -0
- package/dist/context-files/resolver.js.map +1 -0
- package/dist/context-files/types.d.ts +94 -0
- package/dist/context-files/types.d.ts.map +1 -0
- package/dist/context-files/types.js +10 -0
- package/dist/context-files/types.js.map +1 -0
- package/dist/context-files/watcher-integration.d.ts +47 -0
- package/dist/context-files/watcher-integration.d.ts.map +1 -0
- package/dist/context-files/watcher-integration.js +47 -0
- package/dist/context-files/watcher-integration.js.map +1 -0
- package/dist/core/__tests__/conditional-redirects.test.d.ts +2 -0
- package/dist/core/__tests__/conditional-redirects.test.d.ts.map +1 -0
- package/dist/core/__tests__/conditional-redirects.test.js +83 -0
- package/dist/core/__tests__/conditional-redirects.test.js.map +1 -0
- package/dist/core/__tests__/connections.test.d.ts +2 -0
- package/dist/core/__tests__/connections.test.d.ts.map +1 -0
- package/dist/core/__tests__/connections.test.js +158 -0
- package/dist/core/__tests__/connections.test.js.map +1 -0
- package/dist/core/__tests__/hash.test.d.ts +2 -0
- package/dist/core/__tests__/hash.test.d.ts.map +1 -0
- package/dist/core/__tests__/hash.test.js +139 -0
- package/dist/core/__tests__/hash.test.js.map +1 -0
- package/dist/core/__tests__/id.test.d.ts +2 -0
- package/dist/core/__tests__/id.test.d.ts.map +1 -0
- package/dist/core/__tests__/id.test.js +142 -0
- package/dist/core/__tests__/id.test.js.map +1 -0
- package/dist/core/__tests__/location.test.d.ts +2 -0
- package/dist/core/__tests__/location.test.d.ts.map +1 -0
- package/dist/core/__tests__/location.test.js +77 -0
- package/dist/core/__tests__/location.test.js.map +1 -0
- package/dist/core/__tests__/merge-driver.test.d.ts +2 -0
- package/dist/core/__tests__/merge-driver.test.d.ts.map +1 -0
- package/dist/core/__tests__/merge-driver.test.js +218 -0
- package/dist/core/__tests__/merge-driver.test.js.map +1 -0
- package/dist/core/__tests__/redirects.test.d.ts +2 -0
- package/dist/core/__tests__/redirects.test.d.ts.map +1 -0
- package/dist/core/__tests__/redirects.test.js +123 -0
- package/dist/core/__tests__/redirects.test.js.map +1 -0
- package/dist/core/__tests__/resolve-location-target.test.d.ts +8 -0
- package/dist/core/__tests__/resolve-location-target.test.d.ts.map +1 -0
- package/dist/core/__tests__/resolve-location-target.test.js +303 -0
- package/dist/core/__tests__/resolve-location-target.test.js.map +1 -0
- package/dist/core/__tests__/uri.test.d.ts +2 -0
- package/dist/core/__tests__/uri.test.d.ts.map +1 -0
- package/dist/core/__tests__/uri.test.js +159 -0
- package/dist/core/__tests__/uri.test.js.map +1 -0
- package/dist/core/__tests__/worktree.test.d.ts +2 -0
- package/dist/core/__tests__/worktree.test.d.ts.map +1 -0
- package/dist/core/__tests__/worktree.test.js +120 -0
- package/dist/core/__tests__/worktree.test.js.map +1 -0
- package/dist/core/merge-driver.d.ts +5 -0
- package/dist/core/merge-driver.d.ts.map +1 -1
- package/dist/core/merge-driver.js +35 -0
- package/dist/core/merge-driver.js.map +1 -1
- package/dist/daemon/__tests__/flush.test.d.ts +5 -0
- package/dist/daemon/__tests__/flush.test.d.ts.map +1 -0
- package/dist/daemon/__tests__/flush.test.js +213 -0
- package/dist/daemon/__tests__/flush.test.js.map +1 -0
- package/dist/daemon/__tests__/integration.test.d.ts +7 -0
- package/dist/daemon/__tests__/integration.test.d.ts.map +1 -0
- package/dist/daemon/__tests__/integration.test.js +276 -0
- package/dist/daemon/__tests__/integration.test.js.map +1 -0
- package/dist/daemon/__tests__/ipc.test.d.ts +5 -0
- package/dist/daemon/__tests__/ipc.test.d.ts.map +1 -0
- package/dist/daemon/__tests__/ipc.test.js +314 -0
- package/dist/daemon/__tests__/ipc.test.js.map +1 -0
- package/dist/daemon/__tests__/lifecycle.test.d.ts +5 -0
- package/dist/daemon/__tests__/lifecycle.test.d.ts.map +1 -0
- package/dist/daemon/__tests__/lifecycle.test.js +301 -0
- package/dist/daemon/__tests__/lifecycle.test.js.map +1 -0
- package/dist/daemon/__tests__/lock.test.d.ts +5 -0
- package/dist/daemon/__tests__/lock.test.d.ts.map +1 -0
- package/dist/daemon/__tests__/lock.test.js +192 -0
- package/dist/daemon/__tests__/lock.test.js.map +1 -0
- package/dist/daemon/__tests__/methods/graph.test.d.ts +5 -0
- package/dist/daemon/__tests__/methods/graph.test.d.ts.map +1 -0
- package/dist/daemon/__tests__/methods/graph.test.js +309 -0
- package/dist/daemon/__tests__/methods/graph.test.js.map +1 -0
- package/dist/daemon/__tests__/methods/provider.test.d.ts +7 -0
- package/dist/daemon/__tests__/methods/provider.test.d.ts.map +1 -0
- package/dist/daemon/__tests__/methods/provider.test.js +181 -0
- package/dist/daemon/__tests__/methods/provider.test.js.map +1 -0
- package/dist/daemon/__tests__/methods/tools.test.d.ts +5 -0
- package/dist/daemon/__tests__/methods/tools.test.d.ts.map +1 -0
- package/dist/daemon/__tests__/methods/tools.test.js +587 -0
- package/dist/daemon/__tests__/methods/tools.test.js.map +1 -0
- package/dist/daemon/__tests__/multi-location.test.d.ts +8 -0
- package/dist/daemon/__tests__/multi-location.test.d.ts.map +1 -0
- package/dist/daemon/__tests__/multi-location.test.js +669 -0
- package/dist/daemon/__tests__/multi-location.test.js.map +1 -0
- package/dist/daemon/__tests__/registry.test.d.ts +5 -0
- package/dist/daemon/__tests__/registry.test.d.ts.map +1 -0
- package/dist/daemon/__tests__/registry.test.js +208 -0
- package/dist/daemon/__tests__/registry.test.js.map +1 -0
- package/dist/daemon/__tests__/watcher.test.d.ts +5 -0
- package/dist/daemon/__tests__/watcher.test.d.ts.map +1 -0
- package/dist/daemon/__tests__/watcher.test.js +234 -0
- package/dist/daemon/__tests__/watcher.test.js.map +1 -0
- package/dist/daemon/index.d.ts +6 -4
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +3 -2
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/ipc.d.ts.map +1 -1
- package/dist/daemon/ipc.js +8 -1
- package/dist/daemon/ipc.js.map +1 -1
- package/dist/daemon/lifecycle.d.ts.map +1 -1
- package/dist/daemon/lifecycle.js +138 -25
- package/dist/daemon/lifecycle.js.map +1 -1
- package/dist/daemon/location-state.d.ts +12 -6
- package/dist/daemon/location-state.d.ts.map +1 -1
- package/dist/daemon/location-state.js +50 -20
- package/dist/daemon/location-state.js.map +1 -1
- package/dist/daemon/methods/__tests__/graph.test.d.ts +5 -0
- package/dist/daemon/methods/__tests__/graph.test.d.ts.map +1 -0
- package/dist/daemon/methods/__tests__/graph.test.js +274 -0
- package/dist/daemon/methods/__tests__/graph.test.js.map +1 -0
- package/dist/daemon/methods/__tests__/provider.test.d.ts +5 -0
- package/dist/daemon/methods/__tests__/provider.test.d.ts.map +1 -0
- package/dist/daemon/methods/__tests__/provider.test.js +184 -0
- package/dist/daemon/methods/__tests__/provider.test.js.map +1 -0
- package/dist/daemon/methods/__tests__/tools.test.d.ts +5 -0
- package/dist/daemon/methods/__tests__/tools.test.d.ts.map +1 -0
- package/dist/daemon/methods/__tests__/tools.test.js +295 -0
- package/dist/daemon/methods/__tests__/tools.test.js.map +1 -0
- package/dist/daemon/methods/context-files.d.ts +14 -0
- package/dist/daemon/methods/context-files.d.ts.map +1 -0
- package/dist/daemon/methods/context-files.js +95 -0
- package/dist/daemon/methods/context-files.js.map +1 -0
- package/dist/daemon/methods/provider.d.ts.map +1 -1
- package/dist/daemon/methods/provider.js +15 -0
- package/dist/daemon/methods/provider.js.map +1 -1
- package/dist/daemon/methods/tools.d.ts +1 -1
- package/dist/daemon/methods/tools.d.ts.map +1 -1
- package/dist/daemon/methods/tools.js +3 -2
- package/dist/daemon/methods/tools.js.map +1 -1
- package/dist/daemon/sessionlog-linker.d.ts +71 -0
- package/dist/daemon/sessionlog-linker.d.ts.map +1 -0
- package/dist/daemon/sessionlog-linker.js +472 -0
- package/dist/daemon/sessionlog-linker.js.map +1 -0
- package/dist/daemon/sessionlog-watcher.d.ts +79 -0
- package/dist/daemon/sessionlog-watcher.d.ts.map +1 -0
- package/dist/daemon/sessionlog-watcher.js +289 -0
- package/dist/daemon/sessionlog-watcher.js.map +1 -0
- package/dist/graph/__tests__/EdgeTypeRegistry.test.d.ts +2 -0
- package/dist/graph/__tests__/EdgeTypeRegistry.test.d.ts.map +1 -0
- package/dist/graph/__tests__/EdgeTypeRegistry.test.js +212 -0
- package/dist/graph/__tests__/EdgeTypeRegistry.test.js.map +1 -0
- package/dist/graph/__tests__/FederatedGraph.test.d.ts +2 -0
- package/dist/graph/__tests__/FederatedGraph.test.d.ts.map +1 -0
- package/dist/graph/__tests__/FederatedGraph.test.js +661 -0
- package/dist/graph/__tests__/FederatedGraph.test.js.map +1 -0
- package/dist/graph/__tests__/GraphologyAdapter.test.d.ts +2 -0
- package/dist/graph/__tests__/GraphologyAdapter.test.d.ts.map +1 -0
- package/dist/graph/__tests__/GraphologyAdapter.test.js +326 -0
- package/dist/graph/__tests__/GraphologyAdapter.test.js.map +1 -0
- package/dist/graph/__tests__/HydratingFederatedGraph.test.d.ts +2 -0
- package/dist/graph/__tests__/HydratingFederatedGraph.test.d.ts.map +1 -0
- package/dist/graph/__tests__/HydratingFederatedGraph.test.js +587 -0
- package/dist/graph/__tests__/HydratingFederatedGraph.test.js.map +1 -0
- package/dist/graph/__tests__/debounce.test.d.ts +5 -0
- package/dist/graph/__tests__/debounce.test.d.ts.map +1 -0
- package/dist/graph/__tests__/debounce.test.js +195 -0
- package/dist/graph/__tests__/debounce.test.js.map +1 -0
- package/dist/graph/__tests__/edge-cases.test.d.ts +8 -0
- package/dist/graph/__tests__/edge-cases.test.d.ts.map +1 -0
- package/dist/graph/__tests__/edge-cases.test.js +472 -0
- package/dist/graph/__tests__/edge-cases.test.js.map +1 -0
- package/dist/graph/__tests__/expansion.test.d.ts +2 -0
- package/dist/graph/__tests__/expansion.test.d.ts.map +1 -0
- package/dist/graph/__tests__/expansion.test.js +105 -0
- package/dist/graph/__tests__/expansion.test.js.map +1 -0
- package/dist/graph/__tests__/provider-store.test.d.ts +5 -0
- package/dist/graph/__tests__/provider-store.test.d.ts.map +1 -0
- package/dist/graph/__tests__/provider-store.test.js +791 -0
- package/dist/graph/__tests__/provider-store.test.js.map +1 -0
- package/dist/graph/__tests__/query.test.d.ts +5 -0
- package/dist/graph/__tests__/query.test.d.ts.map +1 -0
- package/dist/graph/__tests__/query.test.js +774 -0
- package/dist/graph/__tests__/query.test.js.map +1 -0
- package/dist/graph/__tests__/store.test.d.ts +5 -0
- package/dist/graph/__tests__/store.test.d.ts.map +1 -0
- package/dist/graph/__tests__/store.test.js +489 -0
- package/dist/graph/__tests__/store.test.js.map +1 -0
- package/dist/graph/__tests__/sync.test.d.ts +5 -0
- package/dist/graph/__tests__/sync.test.d.ts.map +1 -0
- package/dist/graph/__tests__/sync.test.js +129 -0
- package/dist/graph/__tests__/sync.test.js.map +1 -0
- package/dist/graph/__tests__/validation.test.d.ts +2 -0
- package/dist/graph/__tests__/validation.test.d.ts.map +1 -0
- package/dist/graph/__tests__/validation.test.js +521 -0
- package/dist/graph/__tests__/validation.test.js.map +1 -0
- package/dist/graph/index.d.ts +1 -1
- package/dist/graph/index.d.ts.map +1 -1
- package/dist/graph/index.js.map +1 -1
- package/dist/graph/provider-store.d.ts +78 -4
- package/dist/graph/provider-store.d.ts.map +1 -1
- package/dist/graph/provider-store.js +579 -55
- package/dist/graph/provider-store.js.map +1 -1
- package/dist/graph/store.d.ts.map +1 -1
- package/dist/graph/store.js +3 -0
- package/dist/graph/store.js.map +1 -1
- package/dist/graph/types.d.ts +16 -0
- package/dist/graph/types.d.ts.map +1 -1
- package/dist/graph/types.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/materialization/git-archive-store.js +2 -2
- package/dist/materialization/git-archive-store.js.map +1 -1
- package/dist/materialization/git-remote-store.js +2 -2
- package/dist/materialization/git-remote-store.js.map +1 -1
- package/dist/materialization/snapshot.js +4 -4
- package/dist/materialization/snapshot.js.map +1 -1
- package/dist/mcp/index.d.ts +8 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +8 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +39 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +677 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/stdio.d.ts +14 -0
- package/dist/mcp/stdio.d.ts.map +1 -0
- package/dist/mcp/stdio.js +19 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/providers/__tests__/beads.test.d.ts +5 -0
- package/dist/providers/__tests__/beads.test.d.ts.map +1 -0
- package/dist/providers/__tests__/beads.test.js +591 -0
- package/dist/providers/__tests__/beads.test.js.map +1 -0
- package/dist/providers/__tests__/claude-tasks.test.d.ts +5 -0
- package/dist/providers/__tests__/claude-tasks.test.d.ts.map +1 -0
- package/dist/providers/__tests__/claude-tasks.test.js +392 -0
- package/dist/providers/__tests__/claude-tasks.test.js.map +1 -0
- package/dist/providers/__tests__/from-config.test.d.ts +5 -0
- package/dist/providers/__tests__/from-config.test.d.ts.map +1 -0
- package/dist/providers/__tests__/from-config.test.js +152 -0
- package/dist/providers/__tests__/from-config.test.js.map +1 -0
- package/dist/providers/__tests__/materialization.test.d.ts +5 -0
- package/dist/providers/__tests__/materialization.test.d.ts.map +1 -0
- package/dist/providers/__tests__/materialization.test.js +407 -0
- package/dist/providers/__tests__/materialization.test.js.map +1 -0
- package/dist/providers/__tests__/native.test.d.ts +5 -0
- package/dist/providers/__tests__/native.test.d.ts.map +1 -0
- package/dist/providers/__tests__/native.test.js +566 -0
- package/dist/providers/__tests__/native.test.js.map +1 -0
- package/dist/providers/__tests__/registry.test.d.ts +5 -0
- package/dist/providers/__tests__/registry.test.d.ts.map +1 -0
- package/dist/providers/__tests__/registry.test.js +183 -0
- package/dist/providers/__tests__/registry.test.js.map +1 -0
- package/dist/providers/beads.d.ts.map +1 -1
- package/dist/providers/beads.js +17 -1
- package/dist/providers/beads.js.map +1 -1
- package/dist/providers/claude-tasks-fs.d.ts +22 -0
- package/dist/providers/claude-tasks-fs.d.ts.map +1 -0
- package/dist/providers/claude-tasks-fs.js +158 -0
- package/dist/providers/claude-tasks-fs.js.map +1 -0
- package/dist/providers/claude-tasks.d.ts +13 -4
- package/dist/providers/claude-tasks.d.ts.map +1 -1
- package/dist/providers/claude-tasks.js +318 -5
- package/dist/providers/claude-tasks.js.map +1 -1
- package/dist/providers/from-config.d.ts.map +1 -1
- package/dist/providers/from-config.js +35 -17
- package/dist/providers/from-config.js.map +1 -1
- package/dist/providers/global.d.ts.map +1 -1
- package/dist/providers/global.js +5 -0
- package/dist/providers/global.js.map +1 -1
- package/dist/providers/index.d.ts +6 -4
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +4 -3
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/map.d.ts.map +1 -1
- package/dist/providers/map.js +5 -0
- package/dist/providers/map.js.map +1 -1
- package/dist/providers/materialization.d.ts +23 -5
- package/dist/providers/materialization.d.ts.map +1 -1
- package/dist/providers/materialization.js +95 -4
- package/dist/providers/materialization.js.map +1 -1
- package/dist/providers/native.d.ts.map +1 -1
- package/dist/providers/native.js +5 -0
- package/dist/providers/native.js.map +1 -1
- package/dist/providers/sessionlog.d.ts +81 -0
- package/dist/providers/sessionlog.d.ts.map +1 -0
- package/dist/providers/sessionlog.js +478 -0
- package/dist/providers/sessionlog.js.map +1 -0
- package/dist/providers/sudocode.d.ts.map +1 -1
- package/dist/providers/sudocode.js +17 -1
- package/dist/providers/sudocode.js.map +1 -1
- package/dist/providers/traits/Reconcilable.d.ts +57 -0
- package/dist/providers/traits/Reconcilable.d.ts.map +1 -0
- package/dist/providers/traits/Reconcilable.js +18 -0
- package/dist/providers/traits/Reconcilable.js.map +1 -0
- package/dist/providers/traits/__tests__/RelationshipQueryable.test.d.ts +2 -0
- package/dist/providers/traits/__tests__/RelationshipQueryable.test.d.ts.map +1 -0
- package/dist/providers/traits/__tests__/RelationshipQueryable.test.js +169 -0
- package/dist/providers/traits/__tests__/RelationshipQueryable.test.js.map +1 -0
- package/dist/providers/traits/__tests__/TaskManageable.test.d.ts +2 -0
- package/dist/providers/traits/__tests__/TaskManageable.test.d.ts.map +1 -0
- package/dist/providers/traits/__tests__/TaskManageable.test.js +172 -0
- package/dist/providers/traits/__tests__/TaskManageable.test.js.map +1 -0
- package/dist/providers/traits/index.d.ts +2 -0
- package/dist/providers/traits/index.d.ts.map +1 -1
- package/dist/providers/traits/index.js +1 -0
- package/dist/providers/traits/index.js.map +1 -1
- package/dist/providers/types.d.ts +63 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js +22 -0
- package/dist/providers/types.js.map +1 -1
- package/dist/schema/__tests__/validation.test.d.ts +2 -0
- package/dist/schema/__tests__/validation.test.d.ts.map +1 -0
- package/dist/schema/__tests__/validation.test.js +241 -0
- package/dist/schema/__tests__/validation.test.js.map +1 -0
- package/dist/schema/edges.d.ts +3 -3
- package/dist/schema/edges.d.ts.map +1 -1
- package/dist/sessionlog/agent/agents/claude-code.d.ts +76 -0
- package/dist/sessionlog/agent/agents/claude-code.d.ts.map +1 -0
- package/dist/sessionlog/agent/agents/claude-code.js +759 -0
- package/dist/sessionlog/agent/agents/claude-code.js.map +1 -0
- package/dist/sessionlog/agent/agents/cursor.d.ts +35 -0
- package/dist/sessionlog/agent/agents/cursor.d.ts.map +1 -0
- package/dist/sessionlog/agent/agents/cursor.js +294 -0
- package/dist/sessionlog/agent/agents/cursor.js.map +1 -0
- package/dist/sessionlog/agent/agents/gemini-cli.d.ts +62 -0
- package/dist/sessionlog/agent/agents/gemini-cli.d.ts.map +1 -0
- package/dist/sessionlog/agent/agents/gemini-cli.js +462 -0
- package/dist/sessionlog/agent/agents/gemini-cli.js.map +1 -0
- package/dist/sessionlog/agent/agents/opencode.d.ts +100 -0
- package/dist/sessionlog/agent/agents/opencode.d.ts.map +1 -0
- package/dist/sessionlog/agent/agents/opencode.js +423 -0
- package/dist/sessionlog/agent/agents/opencode.js.map +1 -0
- package/dist/sessionlog/agent/registry.d.ts +54 -0
- package/dist/sessionlog/agent/registry.d.ts.map +1 -0
- package/dist/sessionlog/agent/registry.js +123 -0
- package/dist/sessionlog/agent/registry.js.map +1 -0
- package/dist/sessionlog/agent/session-types.d.ts +45 -0
- package/dist/sessionlog/agent/session-types.d.ts.map +1 -0
- package/dist/sessionlog/agent/session-types.js +50 -0
- package/dist/sessionlog/agent/session-types.js.map +1 -0
- package/dist/sessionlog/agent/types.d.ts +126 -0
- package/dist/sessionlog/agent/types.d.ts.map +1 -0
- package/dist/sessionlog/agent/types.js +39 -0
- package/dist/sessionlog/agent/types.js.map +1 -0
- package/dist/sessionlog/commands/clean.d.ts +40 -0
- package/dist/sessionlog/commands/clean.d.ts.map +1 -0
- package/dist/sessionlog/commands/clean.js +105 -0
- package/dist/sessionlog/commands/clean.js.map +1 -0
- package/dist/sessionlog/commands/disable.d.ts +23 -0
- package/dist/sessionlog/commands/disable.d.ts.map +1 -0
- package/dist/sessionlog/commands/disable.js +57 -0
- package/dist/sessionlog/commands/disable.js.map +1 -0
- package/dist/sessionlog/commands/doctor.d.ts +43 -0
- package/dist/sessionlog/commands/doctor.d.ts.map +1 -0
- package/dist/sessionlog/commands/doctor.js +97 -0
- package/dist/sessionlog/commands/doctor.js.map +1 -0
- package/dist/sessionlog/commands/enable.d.ts +31 -0
- package/dist/sessionlog/commands/enable.d.ts.map +1 -0
- package/dist/sessionlog/commands/enable.js +102 -0
- package/dist/sessionlog/commands/enable.js.map +1 -0
- package/dist/sessionlog/commands/explain.d.ts +69 -0
- package/dist/sessionlog/commands/explain.d.ts.map +1 -0
- package/dist/sessionlog/commands/explain.js +185 -0
- package/dist/sessionlog/commands/explain.js.map +1 -0
- package/dist/sessionlog/commands/reset.d.ts +23 -0
- package/dist/sessionlog/commands/reset.d.ts.map +1 -0
- package/dist/sessionlog/commands/reset.js +68 -0
- package/dist/sessionlog/commands/reset.js.map +1 -0
- package/dist/sessionlog/commands/resume.d.ts +42 -0
- package/dist/sessionlog/commands/resume.d.ts.map +1 -0
- package/dist/sessionlog/commands/resume.js +134 -0
- package/dist/sessionlog/commands/resume.js.map +1 -0
- package/dist/sessionlog/commands/rewind.d.ts +40 -0
- package/dist/sessionlog/commands/rewind.d.ts.map +1 -0
- package/dist/sessionlog/commands/rewind.js +155 -0
- package/dist/sessionlog/commands/rewind.js.map +1 -0
- package/dist/sessionlog/commands/status.d.ts +54 -0
- package/dist/sessionlog/commands/status.d.ts.map +1 -0
- package/dist/sessionlog/commands/status.js +95 -0
- package/dist/sessionlog/commands/status.js.map +1 -0
- package/dist/sessionlog/config.d.ts +40 -0
- package/dist/sessionlog/config.d.ts.map +1 -0
- package/dist/sessionlog/config.js +126 -0
- package/dist/sessionlog/config.js.map +1 -0
- package/dist/sessionlog/git-operations.d.ts +173 -0
- package/dist/sessionlog/git-operations.d.ts.map +1 -0
- package/dist/sessionlog/git-operations.js +399 -0
- package/dist/sessionlog/git-operations.js.map +1 -0
- package/dist/sessionlog/hooks/git-hooks.d.ts +22 -0
- package/dist/sessionlog/hooks/git-hooks.d.ts.map +1 -0
- package/dist/sessionlog/hooks/git-hooks.js +145 -0
- package/dist/sessionlog/hooks/git-hooks.js.map +1 -0
- package/dist/sessionlog/hooks/lifecycle.d.ts +21 -0
- package/dist/sessionlog/hooks/lifecycle.d.ts.map +1 -0
- package/dist/sessionlog/hooks/lifecycle.js +179 -0
- package/dist/sessionlog/hooks/lifecycle.js.map +1 -0
- package/dist/sessionlog/index.d.ts +69 -0
- package/dist/sessionlog/index.d.ts.map +1 -0
- package/dist/sessionlog/index.js +154 -0
- package/dist/sessionlog/index.js.map +1 -0
- package/dist/sessionlog/security/redaction.d.ts +35 -0
- package/dist/sessionlog/security/redaction.d.ts.map +1 -0
- package/dist/sessionlog/security/redaction.js +221 -0
- package/dist/sessionlog/security/redaction.js.map +1 -0
- package/dist/sessionlog/session/state-machine.d.ts +90 -0
- package/dist/sessionlog/session/state-machine.d.ts.map +1 -0
- package/dist/sessionlog/session/state-machine.js +347 -0
- package/dist/sessionlog/session/state-machine.js.map +1 -0
- package/dist/sessionlog/store/checkpoint-store.d.ts +47 -0
- package/dist/sessionlog/store/checkpoint-store.d.ts.map +1 -0
- package/dist/sessionlog/store/checkpoint-store.js +309 -0
- package/dist/sessionlog/store/checkpoint-store.js.map +1 -0
- package/dist/sessionlog/store/native-store.d.ts +21 -0
- package/dist/sessionlog/store/native-store.d.ts.map +1 -0
- package/dist/sessionlog/store/native-store.js +162 -0
- package/dist/sessionlog/store/native-store.js.map +1 -0
- package/dist/sessionlog/store/provider-types.d.ts +78 -0
- package/dist/sessionlog/store/provider-types.d.ts.map +1 -0
- package/dist/sessionlog/store/provider-types.js +12 -0
- package/dist/sessionlog/store/provider-types.js.map +1 -0
- package/dist/sessionlog/store/session-store.d.ts +28 -0
- package/dist/sessionlog/store/session-store.d.ts.map +1 -0
- package/dist/sessionlog/store/session-store.js +187 -0
- package/dist/sessionlog/store/session-store.js.map +1 -0
- package/dist/sessionlog/strategy/attribution.d.ts +39 -0
- package/dist/sessionlog/strategy/attribution.d.ts.map +1 -0
- package/dist/sessionlog/strategy/attribution.js +227 -0
- package/dist/sessionlog/strategy/attribution.js.map +1 -0
- package/dist/sessionlog/strategy/common.d.ts +60 -0
- package/dist/sessionlog/strategy/common.d.ts.map +1 -0
- package/dist/sessionlog/strategy/common.js +162 -0
- package/dist/sessionlog/strategy/common.js.map +1 -0
- package/dist/sessionlog/strategy/content-overlap.d.ts +33 -0
- package/dist/sessionlog/strategy/content-overlap.d.ts.map +1 -0
- package/dist/sessionlog/strategy/content-overlap.js +168 -0
- package/dist/sessionlog/strategy/content-overlap.js.map +1 -0
- package/dist/sessionlog/strategy/manual-commit.d.ts +35 -0
- package/dist/sessionlog/strategy/manual-commit.d.ts.map +1 -0
- package/dist/sessionlog/strategy/manual-commit.js +732 -0
- package/dist/sessionlog/strategy/manual-commit.js.map +1 -0
- package/dist/sessionlog/strategy/types.d.ts +163 -0
- package/dist/sessionlog/strategy/types.d.ts.map +1 -0
- package/dist/sessionlog/strategy/types.js +49 -0
- package/dist/sessionlog/strategy/types.js.map +1 -0
- package/dist/sessionlog/summarize/claude-generator.d.ts +25 -0
- package/dist/sessionlog/summarize/claude-generator.d.ts.map +1 -0
- package/dist/sessionlog/summarize/claude-generator.js +87 -0
- package/dist/sessionlog/summarize/claude-generator.js.map +1 -0
- package/dist/sessionlog/summarize/summarize.d.ts +52 -0
- package/dist/sessionlog/summarize/summarize.d.ts.map +1 -0
- package/dist/sessionlog/summarize/summarize.js +335 -0
- package/dist/sessionlog/summarize/summarize.js.map +1 -0
- package/dist/sessionlog/types.d.ts +298 -0
- package/dist/sessionlog/types.d.ts.map +1 -0
- package/dist/sessionlog/types.js +104 -0
- package/dist/sessionlog/types.js.map +1 -0
- package/dist/sessionlog/utils/chunk-files.d.ts +25 -0
- package/dist/sessionlog/utils/chunk-files.d.ts.map +1 -0
- package/dist/sessionlog/utils/chunk-files.js +47 -0
- package/dist/sessionlog/utils/chunk-files.js.map +1 -0
- package/dist/sessionlog/utils/commit-message.d.ts +11 -0
- package/dist/sessionlog/utils/commit-message.d.ts.map +1 -0
- package/dist/sessionlog/utils/commit-message.js +54 -0
- package/dist/sessionlog/utils/commit-message.js.map +1 -0
- package/dist/sessionlog/utils/detect-agent.d.ts +19 -0
- package/dist/sessionlog/utils/detect-agent.d.ts.map +1 -0
- package/dist/sessionlog/utils/detect-agent.js +34 -0
- package/dist/sessionlog/utils/detect-agent.js.map +1 -0
- package/dist/sessionlog/utils/hook-managers.d.ts +24 -0
- package/dist/sessionlog/utils/hook-managers.d.ts.map +1 -0
- package/dist/sessionlog/utils/hook-managers.js +87 -0
- package/dist/sessionlog/utils/hook-managers.js.map +1 -0
- package/dist/sessionlog/utils/ide-tags.d.ts +12 -0
- package/dist/sessionlog/utils/ide-tags.d.ts.map +1 -0
- package/dist/sessionlog/utils/ide-tags.js +30 -0
- package/dist/sessionlog/utils/ide-tags.js.map +1 -0
- package/dist/sessionlog/utils/paths.d.ts +32 -0
- package/dist/sessionlog/utils/paths.d.ts.map +1 -0
- package/dist/sessionlog/utils/paths.js +55 -0
- package/dist/sessionlog/utils/paths.js.map +1 -0
- package/dist/sessionlog/utils/preview-rewind.d.ts +23 -0
- package/dist/sessionlog/utils/preview-rewind.d.ts.map +1 -0
- package/dist/sessionlog/utils/preview-rewind.js +63 -0
- package/dist/sessionlog/utils/preview-rewind.js.map +1 -0
- package/dist/sessionlog/utils/rewind-conflict.d.ts +52 -0
- package/dist/sessionlog/utils/rewind-conflict.d.ts.map +1 -0
- package/dist/sessionlog/utils/rewind-conflict.js +79 -0
- package/dist/sessionlog/utils/rewind-conflict.js.map +1 -0
- package/dist/sessionlog/utils/shadow-branch.d.ts +53 -0
- package/dist/sessionlog/utils/shadow-branch.d.ts.map +1 -0
- package/dist/sessionlog/utils/shadow-branch.js +97 -0
- package/dist/sessionlog/utils/shadow-branch.js.map +1 -0
- package/dist/sessionlog/utils/string-utils.d.ts +24 -0
- package/dist/sessionlog/utils/string-utils.d.ts.map +1 -0
- package/dist/sessionlog/utils/string-utils.js +47 -0
- package/dist/sessionlog/utils/string-utils.js.map +1 -0
- package/dist/sessionlog/utils/todo-extract.d.ts +52 -0
- package/dist/sessionlog/utils/todo-extract.d.ts.map +1 -0
- package/dist/sessionlog/utils/todo-extract.js +167 -0
- package/dist/sessionlog/utils/todo-extract.js.map +1 -0
- package/dist/sessionlog/utils/trailers.d.ts +36 -0
- package/dist/sessionlog/utils/trailers.d.ts.map +1 -0
- package/dist/sessionlog/utils/trailers.js +149 -0
- package/dist/sessionlog/utils/trailers.js.map +1 -0
- package/dist/sessionlog/utils/transcript-parse.d.ts +57 -0
- package/dist/sessionlog/utils/transcript-parse.d.ts.map +1 -0
- package/dist/sessionlog/utils/transcript-parse.js +126 -0
- package/dist/sessionlog/utils/transcript-parse.js.map +1 -0
- package/dist/sessionlog/utils/transcript-timestamp.d.ts +22 -0
- package/dist/sessionlog/utils/transcript-timestamp.d.ts.map +1 -0
- package/dist/sessionlog/utils/transcript-timestamp.js +56 -0
- package/dist/sessionlog/utils/transcript-timestamp.js.map +1 -0
- package/dist/sessionlog/utils/tree-ops.d.ts +47 -0
- package/dist/sessionlog/utils/tree-ops.d.ts.map +1 -0
- package/dist/sessionlog/utils/tree-ops.js +145 -0
- package/dist/sessionlog/utils/tree-ops.js.map +1 -0
- package/dist/sessionlog/utils/tty.d.ts +25 -0
- package/dist/sessionlog/utils/tty.d.ts.map +1 -0
- package/dist/sessionlog/utils/tty.js +70 -0
- package/dist/sessionlog/utils/tty.js.map +1 -0
- package/dist/sessionlog/utils/validation.d.ts +31 -0
- package/dist/sessionlog/utils/validation.d.ts.map +1 -0
- package/dist/sessionlog/utils/validation.js +59 -0
- package/dist/sessionlog/utils/validation.js.map +1 -0
- package/dist/sessionlog/utils/worktree.d.ts +16 -0
- package/dist/sessionlog/utils/worktree.d.ts.map +1 -0
- package/dist/sessionlog/utils/worktree.js +50 -0
- package/dist/sessionlog/utils/worktree.js.map +1 -0
- package/dist/storage/__tests__/atomic-write.test.d.ts +5 -0
- package/dist/storage/__tests__/atomic-write.test.d.ts.map +1 -0
- package/dist/storage/__tests__/atomic-write.test.js +170 -0
- package/dist/storage/__tests__/atomic-write.test.js.map +1 -0
- package/dist/storage/__tests__/file-lock.test.d.ts +2 -0
- package/dist/storage/__tests__/file-lock.test.d.ts.map +1 -0
- package/dist/storage/__tests__/file-lock.test.js +89 -0
- package/dist/storage/__tests__/file-lock.test.js.map +1 -0
- package/dist/storage/__tests__/jsonl.test.d.ts +2 -0
- package/dist/storage/__tests__/jsonl.test.d.ts.map +1 -0
- package/dist/storage/__tests__/jsonl.test.js +228 -0
- package/dist/storage/__tests__/jsonl.test.js.map +1 -0
- package/dist/storage/__tests__/locked-writer.test.d.ts +2 -0
- package/dist/storage/__tests__/locked-writer.test.d.ts.map +1 -0
- package/dist/storage/__tests__/locked-writer.test.js +109 -0
- package/dist/storage/__tests__/locked-writer.test.js.map +1 -0
- package/dist/storage/__tests__/sqlite.test.d.ts +2 -0
- package/dist/storage/__tests__/sqlite.test.d.ts.map +1 -0
- package/dist/storage/__tests__/sqlite.test.js +470 -0
- package/dist/storage/__tests__/sqlite.test.js.map +1 -0
- package/dist/storage/sqlite-schema.d.ts.map +1 -1
- package/dist/storage/sqlite-schema.js +2 -0
- package/dist/storage/sqlite-schema.js.map +1 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +18 -4
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/tools/__tests__/annotate.test.d.ts +5 -0
- package/dist/tools/__tests__/annotate.test.d.ts.map +1 -0
- package/dist/tools/__tests__/annotate.test.js +314 -0
- package/dist/tools/__tests__/annotate.test.js.map +1 -0
- package/dist/tools/__tests__/link.test.d.ts +5 -0
- package/dist/tools/__tests__/link.test.d.ts.map +1 -0
- package/dist/tools/__tests__/link.test.js +245 -0
- package/dist/tools/__tests__/link.test.js.map +1 -0
- package/dist/tools/__tests__/query.test.d.ts +5 -0
- package/dist/tools/__tests__/query.test.d.ts.map +1 -0
- package/dist/tools/__tests__/query.test.js +288 -0
- package/dist/tools/__tests__/query.test.js.map +1 -0
- package/dist/tools/__tests__/task.test.d.ts +5 -0
- package/dist/tools/__tests__/task.test.d.ts.map +1 -0
- package/dist/tools/__tests__/task.test.js +178 -0
- package/dist/tools/__tests__/task.test.js.map +1 -0
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/query.d.ts +2 -1
- package/dist/tools/query.d.ts.map +1 -1
- package/dist/tools/query.js +217 -7
- package/dist/tools/query.js.map +1 -1
- package/dist/tools/types.d.ts +57 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js.map +1 -1
- package/dist/tracking/claude-task-reconstructor.d.ts +41 -0
- package/dist/tracking/claude-task-reconstructor.d.ts.map +1 -0
- package/dist/tracking/claude-task-reconstructor.js +91 -0
- package/dist/tracking/claude-task-reconstructor.js.map +1 -0
- package/dist/tracking/plan-mode-tracker.d.ts +20 -0
- package/dist/tracking/plan-mode-tracker.d.ts.map +1 -0
- package/dist/tracking/plan-mode-tracker.js +35 -0
- package/dist/tracking/plan-mode-tracker.js.map +1 -0
- package/dist/tracking/transcript-extractor.d.ts +3 -3
- package/dist/tracking/transcript-extractor.d.ts.map +1 -1
- package/dist/tracking/transcript-extractor.js +1 -1
- package/dist/tracking/transcript-extractor.js.map +1 -1
- 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
|
|
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
|
-
|
|
69
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
336
|
-
const
|
|
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: ${
|
|
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(
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
362
|
-
const
|
|
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: ${
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
473
|
-
|
|
474
|
-
const provider = registry.resolveProvider(
|
|
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: ${
|
|
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(
|
|
964
|
+
const parsed = provider.parseUri(taskProviderUri);
|
|
482
965
|
if (!parsed) {
|
|
483
|
-
throw new ProviderError('INVALID_URI', `Cannot parse URI: ${
|
|
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-
|
|
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
|
-
|
|
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
|
|
523
|
-
|
|
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
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
const provider = registry.resolveProvider(
|
|
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;
|