fuli-context 0.5.0 → 0.7.3

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 (206) hide show
  1. package/README.md +370 -202
  2. package/README.zh-CN.md +448 -0
  3. package/compose.graphiti.yml +6 -6
  4. package/dist/web/assets/AboutPage-DXh7Fwxt.css +1 -0
  5. package/dist/web/assets/AboutPage-qeO_vm0T.js +1 -0
  6. package/dist/web/assets/ConnectionsPage-CBHAdZXT.css +1 -0
  7. package/dist/web/assets/ConnectionsPage-DO0GR9Xg.js +1 -0
  8. package/dist/web/assets/KnowledgeInspector-Bjhdu2mf.css +1 -0
  9. package/dist/web/assets/KnowledgeInspector-DEW5lA1m.js +1 -0
  10. package/dist/web/assets/KnowledgeOrganizerPage-BkM568Ld.js +1 -0
  11. package/dist/web/assets/KnowledgeOrganizerPage-Cx1w1jb2.css +1 -0
  12. package/dist/web/assets/KnowledgePage-BAT4aZIm.js +1 -0
  13. package/dist/web/assets/KnowledgeWorkspace-DyGtBz--.js +4 -0
  14. package/dist/web/assets/OverviewPage-CmCcDCAs.js +1 -0
  15. package/dist/web/assets/PersonalProfilePage-C42mwKN8.js +1 -0
  16. package/dist/web/assets/{PersonalProfilePage-S8ZcG2LU.css → PersonalProfilePage-CdmEzyvZ.css} +1 -1
  17. package/dist/web/assets/PersonalProjectsPage-BwaXR62K.js +1 -0
  18. package/dist/web/assets/PublicProjectsPage-egKBulOq.js +1 -0
  19. package/dist/web/assets/ReviewPage-B3vnZ6gn.js +1 -0
  20. package/dist/web/assets/SearchableSelect-ih69qbcS.js +1 -0
  21. package/dist/web/assets/SettingsPage-C6ouU0sR.css +1 -0
  22. package/dist/web/assets/SettingsPage-oLE8mNs5.js +1 -0
  23. package/dist/web/assets/_plugin-vue_export-helper-BDNMzG2s.js +1 -0
  24. package/dist/web/assets/console-DE40hXGC.js +1 -0
  25. package/dist/web/assets/fuli-logo-Bu9WBuQe.js +1 -0
  26. package/dist/web/assets/fuli-logo-ER1wQ8Kk.png +0 -0
  27. package/dist/web/assets/i18n-CDxBs1sz.js +5 -0
  28. package/dist/web/assets/identity-BCxb1mcy.js +1 -0
  29. package/dist/web/assets/index-BzhNfzU_.js +2 -0
  30. package/dist/web/assets/index-CCOGfZzE.css +1 -0
  31. package/dist/web/assets/model-CCMdt10Q.js +1 -0
  32. package/dist/web/assets/paths-DwHeiGMT.js +1 -0
  33. package/dist/web/assets/useMinimumLoadingDisplay-BpCyBeko.css +1 -0
  34. package/dist/web/assets/useMinimumLoadingDisplay-HdI7XAmu.js +1 -0
  35. package/dist/web/assets/vue-router-Drfl1F8i.js +1 -0
  36. package/dist/web/index.html +9 -6
  37. package/docs/external-knowledge-architecture.md +279 -0
  38. package/examples/external-knowledge/markdown-folder.mjs +235 -0
  39. package/graph-provider/fuli_graph/app.py +57 -1
  40. package/graph-provider/fuli_graph/graph_models.py +3 -0
  41. package/graph-provider/fuli_graph/graph_query.py +62 -16
  42. package/graph-provider/fuli_graph/knowledge_review.py +657 -0
  43. package/graph-provider/fuli_graph/knowledge_review_models.py +133 -0
  44. package/graph-provider/fuli_graph/knowledge_search.py +51 -4
  45. package/graph-provider/fuli_graph/models.py +19 -0
  46. package/graph-provider/fuli_graph/runtime.py +15 -0
  47. package/graph-provider/fuli_graph/store_knowledge.py +2 -1
  48. package/graph-provider/pyproject.toml +1 -1
  49. package/npm-shrinkwrap.json +5 -407
  50. package/package.json +7 -5
  51. package/skills/capturing-session-knowledge/SKILL.md +24 -4
  52. package/skills/flreview/SKILL.md +100 -0
  53. package/skills/flreview/agents/openai.yaml +6 -0
  54. package/src/agent-tools/graph-definitions.js +81 -6
  55. package/src/agent-tools/graph-handlers.js +12 -0
  56. package/src/cli/command-registry.js +3 -3
  57. package/src/cli/local-runtime-command.js +51 -28
  58. package/src/cli/local-runtime-options.js +14 -8
  59. package/src/cli/setup-agent-selection.js +12 -12
  60. package/src/cli/setup-command.js +41 -18
  61. package/src/cli/shell-argument.js +10 -0
  62. package/src/cli/uninstall-command.js +17 -15
  63. package/src/cli/update-command.js +38 -30
  64. package/src/external-knowledge/catalog.js +61 -0
  65. package/src/external-knowledge/conflict-policy.js +58 -0
  66. package/src/external-knowledge/connected-search.js +166 -0
  67. package/src/external-knowledge/connectors/custom.js +133 -0
  68. package/src/external-knowledge/connectors/feishu.js +461 -0
  69. package/src/external-knowledge/connectors/mcp.js +438 -0
  70. package/src/external-knowledge/connectors/notion.js +307 -0
  71. package/src/external-knowledge/connectors/retrieval-api.js +222 -0
  72. package/src/external-knowledge/document-mapping.js +151 -0
  73. package/src/external-knowledge/graph-projection.js +17 -0
  74. package/src/external-knowledge/index.js +8 -0
  75. package/src/external-knowledge/registry.js +91 -0
  76. package/src/external-knowledge/runtime.js +68 -0
  77. package/src/external-knowledge/safe-diagnostic.js +14 -0
  78. package/src/external-knowledge/service.js +811 -0
  79. package/src/graphiti/agent-knowledge-workflows.js +7 -2
  80. package/src/graphiti/federated-application.js +68 -42
  81. package/src/graphiti/knowledge-item-role.js +21 -0
  82. package/src/graphiti/knowledge-review.js +43 -0
  83. package/src/graphiti/project-profile-mapping.js +32 -0
  84. package/src/graphiti/provider-client.js +22 -1
  85. package/src/graphiti/source-marker.js +54 -3
  86. package/src/graphiti/task-knowledge-recall.js +250 -0
  87. package/src/http/api-router.js +23 -108
  88. package/src/http/external-knowledge-api-router.js +104 -0
  89. package/src/http/graph-api-router.js +12 -1
  90. package/src/http/request-policy.js +1 -1
  91. package/src/local-runtime/lifecycle.js +26 -15
  92. package/src/mcp/create-mcp-server.js +4 -1
  93. package/src/mcp/instructions.js +7 -7
  94. package/src/mcp/tool-annotations.js +6 -0
  95. package/src/mcp/tool-result.js +1 -0
  96. package/src/server/application-lifecycle.js +2 -11
  97. package/src/server/blocked-ports.js +11 -0
  98. package/src/server.js +36 -17
  99. package/src/setup/agent-installation-status.js +2 -1
  100. package/src/setup/agents.js +2 -1
  101. package/src/setup/claude-code-config.js +2 -1
  102. package/src/setup/codex-bootstrap.js +12 -7
  103. package/src/setup/container-runtime.js +25 -21
  104. package/src/setup/graph-runtime.js +148 -56
  105. package/src/setup/node-runtime.js +3 -3
  106. package/src/setup/options.js +2 -4
  107. package/src/setup/paths.js +18 -3
  108. package/src/setup/review-skill-adapters.js +49 -0
  109. package/src/setup/setup.js +26 -2
  110. package/src/setup/uninstall.js +4 -2
  111. package/src/system/resource-monitor.js +317 -0
  112. package/src/system/runtime-settings.js +131 -0
  113. package/src/system/system-service.js +119 -0
  114. package/README.en.md +0 -336
  115. package/dist/web/assets/ConnectionsPage-CCKWm-6p.js +0 -1
  116. package/dist/web/assets/KnowledgeInspector-BZkB2wJn.js +0 -1
  117. package/dist/web/assets/KnowledgeInspector-CrGYxEob.css +0 -1
  118. package/dist/web/assets/KnowledgeOrganizerPage-CZAF_OlN.css +0 -1
  119. package/dist/web/assets/KnowledgeOrganizerPage-DlvcLwlL.js +0 -1
  120. package/dist/web/assets/KnowledgePage-CNQXd15y.js +0 -1
  121. package/dist/web/assets/KnowledgeWorkspace-CXPE4GCD.js +0 -4
  122. package/dist/web/assets/OverviewPage-ul7g7HXh.js +0 -1
  123. package/dist/web/assets/PersonalProfilePage-CC40Pw2L.js +0 -1
  124. package/dist/web/assets/PersonalProjectsPage-CPIEcShD.js +0 -1
  125. package/dist/web/assets/PublicProjectsPage-C700sR7z.js +0 -1
  126. package/dist/web/assets/ReviewPage-7MDDUDgA.js +0 -1
  127. package/dist/web/assets/console-B_Ena2DS.js +0 -5
  128. package/dist/web/assets/fuli-logo-DGuyqchQ.png +0 -0
  129. package/dist/web/assets/identity-CjBwYSMX.js +0 -1
  130. package/dist/web/assets/index-CpLDx3of.css +0 -1
  131. package/dist/web/assets/index-DAzeFJSc.js +0 -2
  132. package/dist/web/assets/model-C-5RVIJ2.js +0 -1
  133. package/dist/web/assets/paths-BkULqmGI.js +0 -1
  134. package/dist/web/assets/vue-router-DnD3hUm7.js +0 -1
  135. package/src/agent/candidate-read-service.js +0 -53
  136. package/src/agent/context-pack-service.js +0 -83
  137. package/src/agent/public-read-policy.js +0 -39
  138. package/src/agent/public-read-service.js +0 -69
  139. package/src/agent/read-projection.js +0 -107
  140. package/src/agent/search-service.js +0 -61
  141. package/src/agent/space-boundary.js +0 -33
  142. package/src/app/bootstrap.js +0 -8
  143. package/src/app/create-application.js +0 -143
  144. package/src/app/state-service.js +0 -38
  145. package/src/candidates.js +0 -78
  146. package/src/classifier.js +0 -63
  147. package/src/cli/command-arguments.js +0 -14
  148. package/src/context-pack.js +0 -206
  149. package/src/extractor.js +0 -77
  150. package/src/fact-history.js +0 -39
  151. package/src/facts/fact-service.js +0 -89
  152. package/src/http/bounded-json-array.js +0 -19
  153. package/src/http/lens-route.js +0 -50
  154. package/src/ingestion.js +0 -161
  155. package/src/lens/lens-correction-service.js +0 -81
  156. package/src/lens/lens-fact-writer.js +0 -85
  157. package/src/lens/lens-input.js +0 -62
  158. package/src/lens/lens-query-projection.js +0 -87
  159. package/src/lens/lens-query-ranking.js +0 -62
  160. package/src/lens/lens-query.js +0 -143
  161. package/src/lens/lens-resource-projection.js +0 -93
  162. package/src/lens/lens-resource-selection.js +0 -45
  163. package/src/lens/lens-resource-service.js +0 -103
  164. package/src/lens/lens-search-query.js +0 -122
  165. package/src/lens/lens-service.js +0 -183
  166. package/src/models.js +0 -44
  167. package/src/observer.js +0 -83
  168. package/src/project-rules.js +0 -33
  169. package/src/publication/outbox-service.js +0 -46
  170. package/src/publication/publication-envelope-schema.js +0 -75
  171. package/src/publication/publication-envelope.js +0 -37
  172. package/src/publication/publication-policy.js +0 -82
  173. package/src/publication/publication-service.js +0 -48
  174. package/src/router.js +0 -58
  175. package/src/runtime-options.js +0 -138
  176. package/src/security/episode-visibility.js +0 -14
  177. package/src/security/fact-visibility.js +0 -15
  178. package/src/security/source-metadata-policy.js +0 -8
  179. package/src/storage/episode-evidence-preview.js +0 -100
  180. package/src/storage/fact-search-order.js +0 -18
  181. package/src/storage/file/episode-evidence.js +0 -37
  182. package/src/storage/file/episode-index.js +0 -59
  183. package/src/storage/file/fact-search.js +0 -60
  184. package/src/storage/file-store.js +0 -437
  185. package/src/storage/migrate.js +0 -128
  186. package/src/storage/migrations/001-initial.sql +0 -83
  187. package/src/storage/migrations/002-storage-integrity.sql +0 -113
  188. package/src/storage/record-validation.js +0 -46
  189. package/src/storage/snapshot-defaults.js +0 -54
  190. package/src/storage/sqlite/candidates-repository.js +0 -84
  191. package/src/storage/sqlite/episode-evidence-repository.js +0 -154
  192. package/src/storage/sqlite/episode-privacy-sql.js +0 -33
  193. package/src/storage/sqlite/episodes-repository.js +0 -52
  194. package/src/storage/sqlite/fact-search-repository.js +0 -162
  195. package/src/storage/sqlite/facts-repository.js +0 -146
  196. package/src/storage/sqlite/imports-repository.js +0 -39
  197. package/src/storage/sqlite/mapper.js +0 -84
  198. package/src/storage/sqlite/open-database.js +0 -15
  199. package/src/storage/sqlite/outbox-repository.js +0 -103
  200. package/src/storage/sqlite/snapshot-repository.js +0 -69
  201. package/src/storage/sqlite/spaces-repository.js +0 -52
  202. package/src/storage/sqlite/subscriptions-repository.js +0 -54
  203. package/src/storage/sqlite-store.js +0 -159
  204. package/src/storage/stable-json.js +0 -24
  205. package/src/storage/store-port.js +0 -20
  206. package/src/store.js +0 -2
@@ -0,0 +1,133 @@
1
+ from datetime import datetime
2
+ from typing import Literal
3
+
4
+ from pydantic import Field, model_validator
5
+
6
+ from .models import (
7
+ ConfirmationStatus,
8
+ EpistemicQuadrant,
9
+ KnowledgeFeedbackKind,
10
+ KnowledgeItemKind,
11
+ PersonalProfileAspect,
12
+ PreferenceScope,
13
+ StrictModel,
14
+ )
15
+
16
+ KnowledgeReviewScope = Literal[
17
+ 'all',
18
+ 'preferences_global',
19
+ 'preferences_project',
20
+ 'projects_all',
21
+ 'project',
22
+ ]
23
+ KnowledgeReviewStatus = Literal['active', 'paused', 'completed']
24
+ KnowledgeReviewDisposition = Literal['paused', 'completed']
25
+ KnowledgeReviewOutcome = Literal[
26
+ 'confirmed', 'updated', 'invalidated', 'deferred', 'delegated_to_ai'
27
+ ]
28
+ KnowledgeReviewReason = Literal[
29
+ 'changed_since_last',
30
+ 'deferred_from_previous',
31
+ 'conflict_or_attention',
32
+ 'low_weight',
33
+ 'repeated_cross_session',
34
+ ]
35
+
36
+
37
+ class KnowledgeReviewStart(StrictModel):
38
+ personal_space_id: str = Field(min_length=1, max_length=128)
39
+ scope: KnowledgeReviewScope
40
+ personal_project_id: str | None = Field(default=None, min_length=1, max_length=128)
41
+
42
+ @model_validator(mode='after')
43
+ def validate_scope_project(self):
44
+ needs_project = self.scope in {'preferences_project', 'project'}
45
+ if needs_project and not self.personal_project_id:
46
+ raise ValueError(f'{self.scope} requires personal_project_id')
47
+ if not needs_project and self.personal_project_id:
48
+ raise ValueError(f'{self.scope} does not accept personal_project_id')
49
+ return self
50
+
51
+
52
+ class KnowledgeReviewRun(StrictModel):
53
+ review_id: str
54
+ personal_space_id: str
55
+ scope: KnowledgeReviewScope
56
+ personal_project_id: str | None = None
57
+ scope_key: str
58
+ status: KnowledgeReviewStatus
59
+ previous_completed_at: datetime | None = None
60
+ review_cutoff_at: datetime
61
+ started_at: datetime
62
+ updated_at: datetime
63
+ completed_at: datetime | None = None
64
+ resumed: bool = False
65
+
66
+
67
+ class KnowledgeReviewCandidate(StrictModel):
68
+ candidate_key: str
69
+ item_id: str
70
+ item_kind: KnowledgeItemKind
71
+ title: str
72
+ content: str
73
+ # Keep the decision-critical ranking context near the front of the wire
74
+ # payload. Older MCP clients may project a bounded prefix of the Provider
75
+ # response, and the user must still see why a candidate was selected.
76
+ priority: int = Field(ge=1, le=4)
77
+ reasons: list[KnowledgeReviewReason]
78
+ confirmation_status: ConfirmationStatus
79
+ current_quadrant: EpistemicQuadrant
80
+ project_ids: list[str] = Field(default_factory=list)
81
+ profile_aspect: PersonalProfileAspect | None = None
82
+ preference_scope: PreferenceScope | None = None
83
+ preference_project_id: str | None = None
84
+ utility_score: float = Field(ge=0, le=1)
85
+ confidence_score: float = Field(ge=0, le=1)
86
+ qualified_use_count: int = Field(ge=0)
87
+ distinct_task_count: int = Field(ge=0)
88
+ negative_evidence_count: int = Field(ge=0)
89
+ requires_attention: bool
90
+ last_feedback_kind: KnowledgeFeedbackKind | None = None
91
+ distinct_session_count: int = Field(ge=0)
92
+ changed_at: datetime | None = None
93
+
94
+
95
+ class KnowledgeReviewCandidateRequest(StrictModel):
96
+ personal_space_id: str = Field(min_length=1, max_length=128)
97
+ review_id: str = Field(min_length=1, max_length=256)
98
+ limit: int = Field(default=10, ge=1, le=50)
99
+
100
+
101
+ class KnowledgeReviewCandidatePage(StrictModel):
102
+ review: KnowledgeReviewRun
103
+ candidates: list[KnowledgeReviewCandidate]
104
+ total_candidate_count: int = Field(ge=0)
105
+ remaining_candidate_count: int = Field(ge=0)
106
+
107
+
108
+ class KnowledgeReviewProgress(StrictModel):
109
+ personal_space_id: str = Field(min_length=1, max_length=128)
110
+ review_id: str = Field(min_length=1, max_length=256)
111
+ candidate_key: str = Field(
112
+ min_length=3,
113
+ max_length=520,
114
+ pattern=r'^(entity|relationship):.+$',
115
+ )
116
+ outcome: KnowledgeReviewOutcome
117
+ note: str | None = Field(default=None, min_length=1, max_length=2000)
118
+
119
+
120
+ class KnowledgeReviewDecision(StrictModel):
121
+ decision_id: str
122
+ review_id: str
123
+ candidate_key: str
124
+ outcome: KnowledgeReviewOutcome
125
+ note: str | None = None
126
+ created_at: datetime
127
+ updated_at: datetime
128
+
129
+
130
+ class KnowledgeReviewFinish(StrictModel):
131
+ personal_space_id: str = Field(min_length=1, max_length=128)
132
+ review_id: str = Field(min_length=1, max_length=256)
133
+ disposition: KnowledgeReviewDisposition
@@ -358,6 +358,7 @@ async def _personal_entities(
358
358
  AS confirmation_status,
359
359
  node.fuli_confirmation_basis_json AS confirmation_basis_json,
360
360
  node.fuli_reasoning_summary AS reasoning_summary,
361
+ node.fuli_attributes_json AS attributes_json,
361
362
  node.fuli_profile_aspect AS profile_aspect,
362
363
  CASE WHEN node.fuli_profile_aspect IS NULL THEN NULL
363
364
  ELSE coalesce(node.fuli_preference_scope, 'global') END
@@ -650,6 +651,7 @@ def _entity_search_result(record, score: float, space_id: str) -> EntitySearchRe
650
651
  value.pop('last_feedback_at', None)
651
652
  )
652
653
  for field in (
654
+ 'attributes_json',
653
655
  'assignment_project_id',
654
656
  'episode_project_ids',
655
657
  'has_global_episode',
@@ -699,6 +701,7 @@ def _relevance(query: str, record) -> float:
699
701
  if not needle:
700
702
  return 0.0
701
703
  score = 8.0 if needle in haystack else 0.0
704
+ score = max(score, _alias_relevance(needle, _retrieval_aliases(record)))
702
705
  terms = _terms(needle)
703
706
  if terms:
704
707
  coverage = len(terms & _terms(haystack)) / len(terms)
@@ -733,13 +736,57 @@ def _terms(value: str) -> set[str]:
733
736
  term for term in re.findall(r'[a-z0-9_]+', value)
734
737
  if term not in LATIN_QUERY_FILLERS
735
738
  }
736
- chinese = ''.join(re.findall(r'[\u4e00-\u9fff]', value))
737
- for filler in CHINESE_QUERY_FILLERS:
738
- chinese = chinese.replace(filler, '')
739
- grams = {chinese[index:index + 2] for index in range(max(0, len(chinese) - 1))}
739
+ chinese_segments = re.findall(r'[\u4e00-\u9fff]+', value)
740
+ grams = set()
741
+ for chinese in chinese_segments:
742
+ for filler in CHINESE_QUERY_FILLERS:
743
+ chinese = chinese.replace(filler, '')
744
+ grams.update(
745
+ chinese[index:index + 2]
746
+ for index in range(max(0, len(chinese) - 1))
747
+ )
740
748
  return latin | grams
741
749
 
742
750
 
751
+ def _retrieval_aliases(record) -> list[str]:
752
+ attributes = record.get('attributes')
753
+ if not isinstance(attributes, dict):
754
+ attributes = json_object(record.get('attributes_json'))
755
+ values = (
756
+ attributes.get('searchTerms')
757
+ or attributes.get('search_terms')
758
+ or []
759
+ )
760
+ if isinstance(values, str):
761
+ values = [values]
762
+ if not isinstance(values, list):
763
+ return []
764
+ return [
765
+ value.strip()
766
+ for value in values[:32]
767
+ if isinstance(value, str) and 0 < len(value.strip()) <= 256
768
+ ]
769
+
770
+
771
+ def _alias_relevance(query: str, aliases: list[str]) -> float:
772
+ query_terms = _terms(query)
773
+ best = 0.0
774
+ for alias in aliases:
775
+ normalized_alias = _normalized(alias)
776
+ if not normalized_alias:
777
+ continue
778
+ if normalized_alias in query:
779
+ best = max(best, 8.0)
780
+ continue
781
+ alias_terms = _terms(normalized_alias)
782
+ if len(alias_terms) < 2:
783
+ continue
784
+ coverage = len(alias_terms & query_terms) / len(alias_terms)
785
+ if coverage >= 0.75:
786
+ best = max(best, 6.0 + coverage * 2.0)
787
+ return round(best, 4)
788
+
789
+
743
790
  def _timestamp(value) -> float:
744
791
  native = _native_datetime(value)
745
792
  return native.timestamp() if isinstance(native, datetime) else 0.0
@@ -471,6 +471,25 @@ class EntityInput(StrictModel):
471
471
  def bound_attributes(cls, value: dict[str, Any]) -> dict[str, Any]:
472
472
  if len(value) > 32:
473
473
  raise ValueError('an entity may contain at most 32 attributes')
474
+ for key in ('searchTerms', 'search_terms'):
475
+ if key not in value:
476
+ continue
477
+ terms = value[key]
478
+ if (
479
+ not isinstance(terms, list)
480
+ or not terms
481
+ or len(terms) > 32
482
+ or any(
483
+ not isinstance(term, str)
484
+ or not term.strip()
485
+ or len(term) > 256
486
+ for term in terms
487
+ )
488
+ ):
489
+ raise ValueError(
490
+ 'entity search terms must be 1 to 32 non-empty strings '
491
+ 'of at most 256 characters'
492
+ )
474
493
  return value
475
494
 
476
495
  @model_validator(mode='after')
@@ -169,6 +169,18 @@ class GraphitiRuntime:
169
169
  'CREATE CONSTRAINT fuli_knowledge_audit_id IF NOT EXISTS '
170
170
  'FOR (n:FuliKnowledgeAudit) REQUIRE n.id IS UNIQUE'
171
171
  )
172
+ await self.driver.execute_query(
173
+ 'CREATE CONSTRAINT fuli_knowledge_review_id IF NOT EXISTS '
174
+ 'FOR (n:FuliKnowledgeReviewRun) REQUIRE n.id IS UNIQUE'
175
+ )
176
+ await self.driver.execute_query(
177
+ 'CREATE CONSTRAINT fuli_knowledge_review_active IF NOT EXISTS '
178
+ 'FOR (n:FuliKnowledgeReviewRun) REQUIRE n.active_key IS UNIQUE'
179
+ )
180
+ await self.driver.execute_query(
181
+ 'CREATE CONSTRAINT fuli_knowledge_review_decision_id IF NOT EXISTS '
182
+ 'FOR (n:FuliKnowledgeReviewDecision) REQUIRE n.id IS UNIQUE'
183
+ )
172
184
  for query in (
173
185
  'CREATE INDEX fuli_entity_confirmation IF NOT EXISTS '
174
186
  'FOR (n:Entity) ON (n.group_id, n.fuli_confirmation_status)',
@@ -188,6 +200,9 @@ class GraphitiRuntime:
188
200
  'CREATE INDEX fuli_knowledge_audit_lookup IF NOT EXISTS '
189
201
  'FOR (n:FuliKnowledgeAudit) '
190
202
  'ON (n.space_id, n.item_id, n.usage_generation)',
203
+ 'CREATE INDEX fuli_knowledge_review_scope IF NOT EXISTS '
204
+ 'FOR (n:FuliKnowledgeReviewRun) '
205
+ 'ON (n.personal_space_id, n.scope_key, n.status)',
191
206
  'CREATE INDEX fuli_personal_project_relation_type IF NOT EXISTS '
192
207
  'FOR ()-[r:PERSONAL_PROJECT_RELATION]-() ON (r.relation_type)',
193
208
  ):
@@ -44,9 +44,10 @@ class StoreKnowledge:
44
44
  space_id: str,
45
45
  limit: int | None = None,
46
46
  personal_project_id: str | None = None,
47
+ offset: int | None = None,
47
48
  ) -> GraphResult:
48
49
  return await read_graph(
49
- self, actor, space_id, limit, personal_project_id
50
+ self, actor, space_id, limit, personal_project_id, offset
50
51
  )
51
52
 
52
53
  async def collaboration_context(
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fuli-graph-provider"
3
- version = "0.5.0"
3
+ version = "0.7.3"
4
4
  description = "Graphiti-backed personal and shared project provider for Fuli."
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [