fuli-context 0.5.0 → 0.7.2

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.en.md +2 -333
  2. package/README.md +370 -202
  3. package/README.zh-CN.md +448 -0
  4. package/compose.graphiti.yml +6 -6
  5. package/dist/web/assets/AboutPage-DXh7Fwxt.css +1 -0
  6. package/dist/web/assets/AboutPage-DfnZo7Lo.js +1 -0
  7. package/dist/web/assets/ConnectionsPage-BloaSKmt.js +1 -0
  8. package/dist/web/assets/ConnectionsPage-CBHAdZXT.css +1 -0
  9. package/dist/web/assets/KnowledgeInspector-Bjhdu2mf.css +1 -0
  10. package/dist/web/assets/KnowledgeInspector-QvNpv0_k.js +1 -0
  11. package/dist/web/assets/KnowledgeOrganizerPage-Cx1w1jb2.css +1 -0
  12. package/dist/web/assets/KnowledgeOrganizerPage-DKq1jVD5.js +1 -0
  13. package/dist/web/assets/KnowledgePage-BWL0jT01.js +1 -0
  14. package/dist/web/assets/KnowledgeWorkspace-Czq6aZ7b.js +4 -0
  15. package/dist/web/assets/OverviewPage-5sJoO1Gh.js +1 -0
  16. package/dist/web/assets/{PersonalProfilePage-S8ZcG2LU.css → PersonalProfilePage-CdmEzyvZ.css} +1 -1
  17. package/dist/web/assets/PersonalProfilePage-tVChM9ik.js +1 -0
  18. package/dist/web/assets/PersonalProjectsPage-BPFrup4V.js +1 -0
  19. package/dist/web/assets/PublicProjectsPage-BPJSeREN.js +1 -0
  20. package/dist/web/assets/ReviewPage-B7EN817i.js +1 -0
  21. package/dist/web/assets/SearchableSelect-ClLN0rIb.js +1 -0
  22. package/dist/web/assets/SettingsPage-C6ouU0sR.css +1 -0
  23. package/dist/web/assets/SettingsPage-Chd_B3eH.js +1 -0
  24. package/dist/web/assets/_plugin-vue_export-helper-BDNMzG2s.js +1 -0
  25. package/dist/web/assets/console-CbDsKrsO.js +1 -0
  26. package/dist/web/assets/fuli-logo-Bu9WBuQe.js +1 -0
  27. package/dist/web/assets/fuli-logo-ER1wQ8Kk.png +0 -0
  28. package/dist/web/assets/i18n-BQcns1PH.js +5 -0
  29. package/dist/web/assets/identity-B8K4VYg6.js +1 -0
  30. package/dist/web/assets/index-CB8Xmsc2.js +2 -0
  31. package/dist/web/assets/index-CZeuPh-U.css +1 -0
  32. package/dist/web/assets/model-eF52GgPF.js +1 -0
  33. package/dist/web/assets/paths-hwz-Ac1g.js +1 -0
  34. package/dist/web/assets/useMinimumLoadingDisplay-BpCyBeko.css +1 -0
  35. package/dist/web/assets/useMinimumLoadingDisplay-wCgGDmHg.js +1 -0
  36. package/dist/web/assets/vue-router-BsEp_XwI.js +1 -0
  37. package/dist/web/index.html +9 -6
  38. package/docs/external-knowledge-architecture.md +279 -0
  39. package/examples/external-knowledge/markdown-folder.mjs +235 -0
  40. package/graph-provider/fuli_graph/app.py +57 -1
  41. package/graph-provider/fuli_graph/graph_models.py +3 -0
  42. package/graph-provider/fuli_graph/graph_query.py +62 -16
  43. package/graph-provider/fuli_graph/knowledge_review.py +612 -0
  44. package/graph-provider/fuli_graph/knowledge_review_models.py +127 -0
  45. package/graph-provider/fuli_graph/knowledge_search.py +51 -4
  46. package/graph-provider/fuli_graph/models.py +19 -0
  47. package/graph-provider/fuli_graph/runtime.py +15 -0
  48. package/graph-provider/fuli_graph/store_knowledge.py +2 -1
  49. package/graph-provider/pyproject.toml +1 -1
  50. package/npm-shrinkwrap.json +5 -407
  51. package/package.json +7 -5
  52. package/skills/capturing-session-knowledge/SKILL.md +24 -4
  53. package/skills/flreview/SKILL.md +73 -0
  54. package/skills/flreview/agents/openai.yaml +6 -0
  55. package/src/agent-tools/graph-definitions.js +79 -4
  56. package/src/agent-tools/graph-handlers.js +12 -0
  57. package/src/cli/command-registry.js +3 -3
  58. package/src/cli/local-runtime-command.js +51 -28
  59. package/src/cli/local-runtime-options.js +14 -8
  60. package/src/cli/setup-agent-selection.js +12 -12
  61. package/src/cli/setup-command.js +41 -18
  62. package/src/cli/shell-argument.js +10 -0
  63. package/src/cli/uninstall-command.js +17 -15
  64. package/src/cli/update-command.js +38 -30
  65. package/src/external-knowledge/catalog.js +61 -0
  66. package/src/external-knowledge/conflict-policy.js +58 -0
  67. package/src/external-knowledge/connected-search.js +166 -0
  68. package/src/external-knowledge/connectors/custom.js +133 -0
  69. package/src/external-knowledge/connectors/feishu.js +461 -0
  70. package/src/external-knowledge/connectors/mcp.js +438 -0
  71. package/src/external-knowledge/connectors/notion.js +307 -0
  72. package/src/external-knowledge/connectors/retrieval-api.js +222 -0
  73. package/src/external-knowledge/document-mapping.js +151 -0
  74. package/src/external-knowledge/graph-projection.js +17 -0
  75. package/src/external-knowledge/index.js +8 -0
  76. package/src/external-knowledge/registry.js +91 -0
  77. package/src/external-knowledge/runtime.js +68 -0
  78. package/src/external-knowledge/safe-diagnostic.js +14 -0
  79. package/src/external-knowledge/service.js +811 -0
  80. package/src/graphiti/agent-knowledge-workflows.js +7 -2
  81. package/src/graphiti/federated-application.js +66 -41
  82. package/src/graphiti/knowledge-item-role.js +21 -0
  83. package/src/graphiti/knowledge-review.js +43 -0
  84. package/src/graphiti/project-profile-mapping.js +32 -0
  85. package/src/graphiti/provider-client.js +22 -1
  86. package/src/graphiti/source-marker.js +54 -3
  87. package/src/graphiti/task-knowledge-recall.js +250 -0
  88. package/src/http/api-router.js +23 -108
  89. package/src/http/external-knowledge-api-router.js +104 -0
  90. package/src/http/graph-api-router.js +12 -1
  91. package/src/http/request-policy.js +1 -1
  92. package/src/local-runtime/lifecycle.js +26 -15
  93. package/src/mcp/create-mcp-server.js +1 -0
  94. package/src/mcp/instructions.js +7 -7
  95. package/src/mcp/tool-annotations.js +6 -0
  96. package/src/mcp/tool-result.js +1 -0
  97. package/src/server/application-lifecycle.js +2 -11
  98. package/src/server/blocked-ports.js +11 -0
  99. package/src/server.js +36 -17
  100. package/src/setup/agent-installation-status.js +2 -1
  101. package/src/setup/agents.js +2 -1
  102. package/src/setup/claude-code-config.js +2 -1
  103. package/src/setup/codex-bootstrap.js +12 -7
  104. package/src/setup/container-runtime.js +25 -21
  105. package/src/setup/graph-runtime.js +148 -56
  106. package/src/setup/node-runtime.js +3 -3
  107. package/src/setup/options.js +2 -4
  108. package/src/setup/paths.js +18 -3
  109. package/src/setup/review-skill-adapters.js +49 -0
  110. package/src/setup/setup.js +26 -2
  111. package/src/setup/uninstall.js +4 -2
  112. package/src/system/resource-monitor.js +317 -0
  113. package/src/system/runtime-settings.js +131 -0
  114. package/src/system/system-service.js +119 -0
  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,127 @@
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
+ KnowledgeFeedbackKind,
9
+ KnowledgeItemKind,
10
+ PersonalProfileAspect,
11
+ PreferenceScope,
12
+ StrictModel,
13
+ )
14
+
15
+ KnowledgeReviewScope = Literal[
16
+ 'all',
17
+ 'preferences_global',
18
+ 'preferences_project',
19
+ 'projects_all',
20
+ 'project',
21
+ ]
22
+ KnowledgeReviewStatus = Literal['active', 'paused', 'completed']
23
+ KnowledgeReviewDisposition = Literal['paused', 'completed']
24
+ KnowledgeReviewOutcome = Literal[
25
+ 'confirmed', 'updated', 'invalidated', 'skipped', 'deferred'
26
+ ]
27
+ KnowledgeReviewReason = Literal[
28
+ 'changed_since_last',
29
+ 'conflict_or_attention',
30
+ 'low_weight',
31
+ 'repeated_cross_session',
32
+ ]
33
+
34
+
35
+ class KnowledgeReviewStart(StrictModel):
36
+ personal_space_id: str = Field(min_length=1, max_length=128)
37
+ scope: KnowledgeReviewScope
38
+ personal_project_id: str | None = Field(default=None, min_length=1, max_length=128)
39
+
40
+ @model_validator(mode='after')
41
+ def validate_scope_project(self):
42
+ needs_project = self.scope in {'preferences_project', 'project'}
43
+ if needs_project and not self.personal_project_id:
44
+ raise ValueError(f'{self.scope} requires personal_project_id')
45
+ if not needs_project and self.personal_project_id:
46
+ raise ValueError(f'{self.scope} does not accept personal_project_id')
47
+ return self
48
+
49
+
50
+ class KnowledgeReviewRun(StrictModel):
51
+ review_id: str
52
+ personal_space_id: str
53
+ scope: KnowledgeReviewScope
54
+ personal_project_id: str | None = None
55
+ scope_key: str
56
+ status: KnowledgeReviewStatus
57
+ previous_completed_at: datetime | None = None
58
+ review_cutoff_at: datetime
59
+ started_at: datetime
60
+ updated_at: datetime
61
+ completed_at: datetime | None = None
62
+ resumed: bool = False
63
+
64
+
65
+ class KnowledgeReviewCandidate(StrictModel):
66
+ candidate_key: str
67
+ item_id: str
68
+ item_kind: KnowledgeItemKind
69
+ title: str
70
+ content: str
71
+ profile_aspect: PersonalProfileAspect | None = None
72
+ preference_scope: PreferenceScope | None = None
73
+ preference_project_id: str | None = None
74
+ project_ids: list[str] = Field(default_factory=list)
75
+ confirmation_status: ConfirmationStatus
76
+ utility_score: float = Field(ge=0, le=1)
77
+ confidence_score: float = Field(ge=0, le=1)
78
+ qualified_use_count: int = Field(ge=0)
79
+ distinct_task_count: int = Field(ge=0)
80
+ negative_evidence_count: int = Field(ge=0)
81
+ requires_attention: bool
82
+ last_feedback_kind: KnowledgeFeedbackKind | None = None
83
+ distinct_session_count: int = Field(ge=0)
84
+ changed_at: datetime | None = None
85
+ priority: int = Field(ge=1, le=4)
86
+ reasons: list[KnowledgeReviewReason]
87
+
88
+
89
+ class KnowledgeReviewCandidateRequest(StrictModel):
90
+ personal_space_id: str = Field(min_length=1, max_length=128)
91
+ review_id: str = Field(min_length=1, max_length=256)
92
+ limit: int = Field(default=10, ge=1, le=50)
93
+
94
+
95
+ class KnowledgeReviewCandidatePage(StrictModel):
96
+ review: KnowledgeReviewRun
97
+ candidates: list[KnowledgeReviewCandidate]
98
+ total_candidate_count: int = Field(ge=0)
99
+ remaining_candidate_count: int = Field(ge=0)
100
+
101
+
102
+ class KnowledgeReviewProgress(StrictModel):
103
+ personal_space_id: str = Field(min_length=1, max_length=128)
104
+ review_id: str = Field(min_length=1, max_length=256)
105
+ candidate_key: str = Field(
106
+ min_length=3,
107
+ max_length=520,
108
+ pattern=r'^(entity|relationship):.+$',
109
+ )
110
+ outcome: KnowledgeReviewOutcome
111
+ note: str | None = Field(default=None, min_length=1, max_length=2000)
112
+
113
+
114
+ class KnowledgeReviewDecision(StrictModel):
115
+ decision_id: str
116
+ review_id: str
117
+ candidate_key: str
118
+ outcome: KnowledgeReviewOutcome
119
+ note: str | None = None
120
+ created_at: datetime
121
+ updated_at: datetime
122
+
123
+
124
+ class KnowledgeReviewFinish(StrictModel):
125
+ personal_space_id: str = Field(min_length=1, max_length=128)
126
+ review_id: str = Field(min_length=1, max_length=256)
127
+ 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.2"
4
4
  description = "Graphiti-backed personal and shared project provider for Fuli."
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [