fuli-context 0.2.0
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/LICENSE +201 -0
- package/README.md +178 -0
- package/compose.graphiti.yml +86 -0
- package/dist/web/assets/ConnectionsPage-CY6H_mJJ.js +1 -0
- package/dist/web/assets/KnowledgeInspector-BSpz5Sfm.js +1 -0
- package/dist/web/assets/KnowledgeInspector-CHbB_0rC.css +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-BlEvhPrv.css +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-CvjGPlUc.js +1 -0
- package/dist/web/assets/KnowledgePage-Khuo3cNs.js +1 -0
- package/dist/web/assets/KnowledgeWorkspace-DVZusvem.js +4 -0
- package/dist/web/assets/OverviewPage-DC4C4Fhn.js +1 -0
- package/dist/web/assets/PersonalProfilePage-CSV9Mtgz.js +1 -0
- package/dist/web/assets/PersonalProfilePage-CVJ3TcRi.css +1 -0
- package/dist/web/assets/PersonalProjectsPage-A50EaseQ.js +1 -0
- package/dist/web/assets/PublicProjectsPage-BAdO_bae.js +1 -0
- package/dist/web/assets/ReviewPage-Dztk2jDG.js +1 -0
- package/dist/web/assets/console-tbvTUi6p.js +1 -0
- package/dist/web/assets/fuli-logo-DGuyqchQ.png +0 -0
- package/dist/web/assets/identity-B3s5CXhv.js +1 -0
- package/dist/web/assets/index-Devf7glZ.css +1 -0
- package/dist/web/assets/index-DwUDhQKc.js +6 -0
- package/dist/web/assets/model-DVT8sjy9.js +1 -0
- package/dist/web/assets/paths-B790iniO.js +1 -0
- package/dist/web/assets/runtime-dom.esm-bundler-DttsK21F.js +1 -0
- package/dist/web/assets/vue-router-BYTAoWT3.js +1 -0
- package/dist/web/index.html +22 -0
- package/graph-provider/Dockerfile +16 -0
- package/graph-provider/fuli_graph/__init__.py +5 -0
- package/graph-provider/fuli_graph/app.py +547 -0
- package/graph-provider/fuli_graph/auth.py +17 -0
- package/graph-provider/fuli_graph/collaboration_context.py +290 -0
- package/graph-provider/fuli_graph/config.py +34 -0
- package/graph-provider/fuli_graph/graph_models.py +123 -0
- package/graph-provider/fuli_graph/graph_projection.py +405 -0
- package/graph-provider/fuli_graph/graph_query.py +561 -0
- package/graph-provider/fuli_graph/knowledge_audit.py +765 -0
- package/graph-provider/fuli_graph/knowledge_batch_confirmation.py +246 -0
- package/graph-provider/fuli_graph/knowledge_management.py +816 -0
- package/graph-provider/fuli_graph/knowledge_records.py +65 -0
- package/graph-provider/fuli_graph/knowledge_search.py +723 -0
- package/graph-provider/fuli_graph/knowledge_usage_models.py +52 -0
- package/graph-provider/fuli_graph/model_validation.py +58 -0
- package/graph-provider/fuli_graph/models.py +1179 -0
- package/graph-provider/fuli_graph/personal_project_access.py +29 -0
- package/graph-provider/fuli_graph/preference_conflicts.py +420 -0
- package/graph-provider/fuli_graph/project_action_models.py +142 -0
- package/graph-provider/fuli_graph/project_knowledge.py +707 -0
- package/graph-provider/fuli_graph/project_management.py +251 -0
- package/graph-provider/fuli_graph/provider_values.py +40 -0
- package/graph-provider/fuli_graph/runtime.py +350 -0
- package/graph-provider/fuli_graph/search_projection.py +156 -0
- package/graph-provider/fuli_graph/store.py +24 -0
- package/graph-provider/fuli_graph/store_identity.py +237 -0
- package/graph-provider/fuli_graph/store_knowledge.py +447 -0
- package/graph-provider/fuli_graph/store_projects.py +242 -0
- package/graph-provider/fuli_graph/store_publication.py +370 -0
- package/graph-provider/fuli_graph/store_records.py +134 -0
- package/graph-provider/pyproject.toml +35 -0
- package/npm-shrinkwrap.json +6045 -0
- package/package.json +99 -0
- package/skills/capturing-session-knowledge/SKILL.md +226 -0
- package/skills/capturing-session-knowledge/agents/openai.yaml +13 -0
- package/skills/grilling-project/SKILL.md +129 -0
- package/skills/grilling-project/agents/openai.yaml +4 -0
- package/skills/grilling-project/references/project-profile.md +105 -0
- package/src/agent/candidate-read-service.js +53 -0
- package/src/agent/context-pack-service.js +83 -0
- package/src/agent/public-read-policy.js +39 -0
- package/src/agent/public-read-service.js +69 -0
- package/src/agent/read-projection.js +107 -0
- package/src/agent/search-service.js +61 -0
- package/src/agent/space-boundary.js +33 -0
- package/src/agent-tools/graph-definitions.js +479 -0
- package/src/agent-tools/graph-handlers.js +96 -0
- package/src/agent-tools/schema.js +37 -0
- package/src/agent-tools.js +16 -0
- package/src/app/application-error.js +13 -0
- package/src/app/bootstrap.js +8 -0
- package/src/app/create-application.js +143 -0
- package/src/app/state-service.js +38 -0
- package/src/candidates.js +78 -0
- package/src/classifier.js +63 -0
- package/src/cli/command-arguments.js +14 -0
- package/src/cli/command-registry.js +78 -0
- package/src/cli/commands/candidates.js +17 -0
- package/src/cli/commands/ingestion.js +32 -0
- package/src/cli/commands/reads.js +50 -0
- package/src/cli/commands/spaces.js +20 -0
- package/src/cli/invocation.js +47 -0
- package/src/cli/local-runtime-command.js +94 -0
- package/src/cli/local-runtime-options.js +64 -0
- package/src/cli/migrate-command.js +71 -0
- package/src/cli/migration-destination.js +117 -0
- package/src/cli/setup-agent-selection.js +195 -0
- package/src/cli/setup-command.js +85 -0
- package/src/cli/uninstall-command.js +67 -0
- package/src/cli.js +90 -0
- package/src/context-pack.js +206 -0
- package/src/defaults.js +1 -0
- package/src/extractor.js +77 -0
- package/src/fact-history.js +39 -0
- package/src/facts/fact-service.js +89 -0
- package/src/graphiti/agent-access-policy.js +88 -0
- package/src/graphiti/capture-policy.js +84 -0
- package/src/graphiti/federated-application.js +1292 -0
- package/src/graphiti/personal-context.js +29 -0
- package/src/graphiti/project-path-context.js +159 -0
- package/src/graphiti/provider-client.js +288 -0
- package/src/graphiti/runtime-config.js +85 -0
- package/src/graphiti/setup.js +43 -0
- package/src/graphiti/source-marker.js +142 -0
- package/src/http/api-router.js +133 -0
- package/src/http/bounded-json-array.js +19 -0
- package/src/http/error-mapping.js +36 -0
- package/src/http/graph-api-router.js +257 -0
- package/src/http/lens-route.js +50 -0
- package/src/http/request-policy.js +41 -0
- package/src/http/response.js +29 -0
- package/src/http/static-handler.js +55 -0
- package/src/ingestion.js +161 -0
- package/src/lens/lens-correction-service.js +81 -0
- package/src/lens/lens-fact-writer.js +85 -0
- package/src/lens/lens-input.js +62 -0
- package/src/lens/lens-query-projection.js +87 -0
- package/src/lens/lens-query-ranking.js +62 -0
- package/src/lens/lens-query.js +143 -0
- package/src/lens/lens-resource-projection.js +93 -0
- package/src/lens/lens-resource-selection.js +45 -0
- package/src/lens/lens-resource-service.js +103 -0
- package/src/lens/lens-search-query.js +122 -0
- package/src/lens/lens-service.js +183 -0
- package/src/local-runtime/lifecycle.js +254 -0
- package/src/mcp/create-mcp-server.js +85 -0
- package/src/mcp/instructions.js +10 -0
- package/src/mcp/project-action-preview-tokens.js +72 -0
- package/src/mcp/runtime.js +71 -0
- package/src/mcp/tool-annotations.js +63 -0
- package/src/mcp/tool-result.js +167 -0
- package/src/mcp/tool-schema.js +60 -0
- package/src/mcp-server.js +76 -0
- package/src/models.js +44 -0
- package/src/observer.js +83 -0
- package/src/package-metadata.js +6 -0
- package/src/project-rules.js +33 -0
- package/src/publication/canonical-json.js +39 -0
- package/src/publication/outbox-service.js +46 -0
- package/src/publication/publication-envelope-schema.js +75 -0
- package/src/publication/publication-envelope.js +37 -0
- package/src/publication/publication-policy.js +82 -0
- package/src/publication/publication-service.js +48 -0
- package/src/router.js +58 -0
- package/src/runtime-options.js +139 -0
- package/src/security/episode-visibility.js +14 -0
- package/src/security/fact-visibility.js +15 -0
- package/src/security/sensitive-content.js +18 -0
- package/src/security/source-metadata-policy.js +8 -0
- package/src/server/application-lifecycle.js +34 -0
- package/src/server/listen.js +24 -0
- package/src/server.js +135 -0
- package/src/setup/agent-installation-status.js +97 -0
- package/src/setup/agents.js +123 -0
- package/src/setup/codex-bootstrap.js +118 -0
- package/src/setup/codex-config.js +122 -0
- package/src/setup/config-backup.js +22 -0
- package/src/setup/container-runtime.js +388 -0
- package/src/setup/cursor-config.js +61 -0
- package/src/setup/graph-runtime.js +360 -0
- package/src/setup/node-runtime.js +24 -0
- package/src/setup/options.js +91 -0
- package/src/setup/paths.js +47 -0
- package/src/setup/session-skill.js +91 -0
- package/src/setup/setup.js +85 -0
- package/src/setup/uninstall.js +90 -0
- package/src/storage/episode-evidence-preview.js +100 -0
- package/src/storage/fact-search-order.js +18 -0
- package/src/storage/file/episode-evidence.js +37 -0
- package/src/storage/file/episode-index.js +59 -0
- package/src/storage/file/fact-search.js +60 -0
- package/src/storage/file-store.js +437 -0
- package/src/storage/import-json.js +70 -0
- package/src/storage/json-file.js +41 -0
- package/src/storage/legacy-record-validation.js +152 -0
- package/src/storage/legacy-snapshot-validation.js +32 -0
- package/src/storage/migrate.js +128 -0
- package/src/storage/migrations/001-initial.sql +83 -0
- package/src/storage/migrations/002-storage-integrity.sql +113 -0
- package/src/storage/record-validation.js +46 -0
- package/src/storage/snapshot-defaults.js +54 -0
- package/src/storage/sqlite/candidates-repository.js +84 -0
- package/src/storage/sqlite/episode-evidence-repository.js +154 -0
- package/src/storage/sqlite/episode-privacy-sql.js +33 -0
- package/src/storage/sqlite/episodes-repository.js +52 -0
- package/src/storage/sqlite/fact-search-repository.js +162 -0
- package/src/storage/sqlite/facts-repository.js +146 -0
- package/src/storage/sqlite/imports-repository.js +39 -0
- package/src/storage/sqlite/mapper.js +84 -0
- package/src/storage/sqlite/open-database.js +15 -0
- package/src/storage/sqlite/outbox-repository.js +103 -0
- package/src/storage/sqlite/snapshot-repository.js +69 -0
- package/src/storage/sqlite/spaces-repository.js +52 -0
- package/src/storage/sqlite/subscriptions-repository.js +54 -0
- package/src/storage/sqlite-store.js +159 -0
- package/src/storage/stable-json.js +24 -0
- package/src/storage/store-port.js +20 -0
- package/src/store.js +2 -0
- package/src/workspace-protocol/array-validation.js +19 -0
- package/src/workspace-protocol/base64url-contract.js +27 -0
- package/src/workspace-protocol/constants.js +37 -0
- package/src/workspace-protocol/error-contract.js +24 -0
- package/src/workspace-protocol/fact-contract.js +110 -0
- package/src/workspace-protocol/governance-contract.js +47 -0
- package/src/workspace-protocol/index.js +53 -0
- package/src/workspace-protocol/json-budget.js +19 -0
- package/src/workspace-protocol/manifest-contract.js +186 -0
- package/src/workspace-protocol/publication-contract.js +212 -0
- package/src/workspace-protocol/query-contract.js +149 -0
- package/src/workspace-protocol/scalar-contract.js +192 -0
- package/src/workspace-protocol/sync-contract.js +184 -0
- package/src/workspace-protocol/text-contract.js +39 -0
- package/src/workspace-protocol/validation.js +66 -0
|
@@ -0,0 +1,1179 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Any, Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import (
|
|
5
|
+
AliasChoices,
|
|
6
|
+
BaseModel,
|
|
7
|
+
ConfigDict,
|
|
8
|
+
Field,
|
|
9
|
+
field_validator,
|
|
10
|
+
model_validator,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from .model_validation import (
|
|
14
|
+
complete_epistemic_state,
|
|
15
|
+
normalize_provider_url,
|
|
16
|
+
reject_credentials,
|
|
17
|
+
require_public_eligible_episode,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
Role = Literal['reader', 'contributor', 'maintainer']
|
|
21
|
+
SpaceKind = Literal['personal', 'project']
|
|
22
|
+
SpaceVisibility = Literal['private', 'public']
|
|
23
|
+
Sensitivity = Literal['normal', 'private', 'restricted']
|
|
24
|
+
ProjectLifecycle = Literal['planned', 'active', 'maintenance', 'archived']
|
|
25
|
+
ProjectSourceKind = Literal[
|
|
26
|
+
'prd',
|
|
27
|
+
'product_document',
|
|
28
|
+
'technical_document',
|
|
29
|
+
'frontend_repository',
|
|
30
|
+
'backend_repository',
|
|
31
|
+
'repository',
|
|
32
|
+
'design',
|
|
33
|
+
'runbook',
|
|
34
|
+
'monitoring',
|
|
35
|
+
'issue_tracker',
|
|
36
|
+
'other',
|
|
37
|
+
]
|
|
38
|
+
AssessmentState = Literal['confirmed', 'inferred']
|
|
39
|
+
AssessmentLabel = Literal[
|
|
40
|
+
'needs_clarification',
|
|
41
|
+
'partially_documented',
|
|
42
|
+
'well_documented',
|
|
43
|
+
]
|
|
44
|
+
ProjectRelationType = Literal[
|
|
45
|
+
'PART_OF',
|
|
46
|
+
'USES_KNOWLEDGE_FROM',
|
|
47
|
+
'DEPENDS_ON',
|
|
48
|
+
'PROVIDES_TO',
|
|
49
|
+
'SHARES_CAPABILITY_WITH',
|
|
50
|
+
'SUCCESSOR_OF',
|
|
51
|
+
'RELATED_TO',
|
|
52
|
+
]
|
|
53
|
+
ProjectRelationStatus = Literal['pending', 'active', 'rejected']
|
|
54
|
+
KnowledgeItemKind = Literal['entity', 'relationship']
|
|
55
|
+
KnowledgeOperationActor = Literal['human', 'agent']
|
|
56
|
+
HumanChangeStatus = Literal['none', 'unseen', 'viewed', 'reviewed']
|
|
57
|
+
KnowledgeAuditAction = Literal[
|
|
58
|
+
'human_change',
|
|
59
|
+
'agent_view',
|
|
60
|
+
'agent_review',
|
|
61
|
+
'knowledge_used',
|
|
62
|
+
]
|
|
63
|
+
KnowledgeUseKind = Literal['cited', 'applied']
|
|
64
|
+
KnowledgeInheritanceMode = Literal[
|
|
65
|
+
'local_only',
|
|
66
|
+
'descendants',
|
|
67
|
+
'selected_projects',
|
|
68
|
+
]
|
|
69
|
+
KnowledgeRevisionAction = Literal[
|
|
70
|
+
'confirm',
|
|
71
|
+
'update',
|
|
72
|
+
'invalidate',
|
|
73
|
+
'link_replacement',
|
|
74
|
+
'restore',
|
|
75
|
+
'scope_change',
|
|
76
|
+
'batch_confirm',
|
|
77
|
+
]
|
|
78
|
+
KnowledgeContentRevisionAction = Literal[
|
|
79
|
+
'confirm',
|
|
80
|
+
'update',
|
|
81
|
+
'invalidate',
|
|
82
|
+
'link_replacement',
|
|
83
|
+
'restore',
|
|
84
|
+
]
|
|
85
|
+
KnowledgeConfirmationGroupKind = Literal['source', 'session']
|
|
86
|
+
EpistemicQuadrant = Literal[
|
|
87
|
+
'known_known',
|
|
88
|
+
'known_unknown',
|
|
89
|
+
'unknown_known',
|
|
90
|
+
'unknown_unknown',
|
|
91
|
+
]
|
|
92
|
+
EpistemicStatus = Literal['confirmed', 'observed', 'exploratory']
|
|
93
|
+
ConfirmationStatus = Literal['confirmed', 'agent_confirmed', 'pending']
|
|
94
|
+
ConfirmationActorKind = Literal['user', 'agent', 'authoritative_source', 'import']
|
|
95
|
+
PersonalProfileAspect = Literal['taste', 'personality', 'judgment_preference']
|
|
96
|
+
PreferenceScope = Literal['global', 'project']
|
|
97
|
+
PreferenceConflictQueueStatus = Literal['ai_pending', 'resolved']
|
|
98
|
+
PreferenceConflictResolutionAction = Literal[
|
|
99
|
+
'merge',
|
|
100
|
+
'keep_left',
|
|
101
|
+
'keep_right',
|
|
102
|
+
'split_scope',
|
|
103
|
+
]
|
|
104
|
+
SourceApplication = Literal['codex', 'claude_code', 'cursor', 'kiro', 'other']
|
|
105
|
+
|
|
106
|
+
class StrictModel(BaseModel):
|
|
107
|
+
model_config = ConfigDict(extra='forbid', str_strip_whitespace=True)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _validate_inheritance(
|
|
111
|
+
mode: KnowledgeInheritanceMode,
|
|
112
|
+
project_ids: list[str],
|
|
113
|
+
profile_aspect: PersonalProfileAspect | None,
|
|
114
|
+
) -> None:
|
|
115
|
+
if len(set(project_ids)) != len(project_ids):
|
|
116
|
+
raise ValueError('inherited project ids must be unique')
|
|
117
|
+
if any(not item or len(item) > 128 for item in project_ids):
|
|
118
|
+
raise ValueError('inherited project ids must contain 1 to 128 characters')
|
|
119
|
+
if mode == 'selected_projects' and not project_ids:
|
|
120
|
+
raise ValueError(
|
|
121
|
+
'selected_projects inheritance requires at least one project id'
|
|
122
|
+
)
|
|
123
|
+
if mode != 'selected_projects' and project_ids:
|
|
124
|
+
raise ValueError(
|
|
125
|
+
'inherited project ids are only valid for selected_projects inheritance'
|
|
126
|
+
)
|
|
127
|
+
if profile_aspect and mode != 'local_only':
|
|
128
|
+
raise ValueError('personal preferences cannot inherit across projects')
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class ConfirmationActor(StrictModel):
|
|
132
|
+
kind: ConfirmationActorKind
|
|
133
|
+
label: str | None = Field(default=None, min_length=1, max_length=160)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class ConfirmationBasis(StrictModel):
|
|
137
|
+
existence_reason: str = Field(min_length=1, max_length=4096)
|
|
138
|
+
quadrant_reason: str = Field(min_length=1, max_length=4096)
|
|
139
|
+
proposed_by: ConfirmationActor
|
|
140
|
+
confirmed_by: ConfirmationActor | None = None
|
|
141
|
+
confirmed_at: datetime | None = None
|
|
142
|
+
agent_policy_version: str | None = Field(
|
|
143
|
+
default=None,
|
|
144
|
+
min_length=1,
|
|
145
|
+
max_length=128,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
@model_validator(mode='after')
|
|
149
|
+
def validate_confirmation_actor(self):
|
|
150
|
+
if bool(self.confirmed_by) != bool(self.confirmed_at):
|
|
151
|
+
raise ValueError('confirmed_by and confirmed_at must be recorded together')
|
|
152
|
+
return self
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _validate_confirmation_state(
|
|
156
|
+
status: ConfirmationStatus,
|
|
157
|
+
basis: ConfirmationBasis,
|
|
158
|
+
) -> None:
|
|
159
|
+
has_confirmation = bool(basis.confirmed_by and basis.confirmed_at)
|
|
160
|
+
if status == 'pending':
|
|
161
|
+
if has_confirmation or basis.agent_policy_version:
|
|
162
|
+
raise ValueError(
|
|
163
|
+
'pending knowledge cannot retain a confirmer, confirmation time, '
|
|
164
|
+
'or agent policy'
|
|
165
|
+
)
|
|
166
|
+
return
|
|
167
|
+
if not has_confirmation:
|
|
168
|
+
raise ValueError(
|
|
169
|
+
f'{status} knowledge requires a confirmer and confirmation time'
|
|
170
|
+
)
|
|
171
|
+
if status == 'confirmed':
|
|
172
|
+
if basis.confirmed_by.kind not in {'user', 'authoritative_source'}:
|
|
173
|
+
raise ValueError(
|
|
174
|
+
'an agent or import cannot confirm human-confirmed knowledge; '
|
|
175
|
+
'a user or authoritative source is required'
|
|
176
|
+
)
|
|
177
|
+
if basis.agent_policy_version:
|
|
178
|
+
raise ValueError(
|
|
179
|
+
'human-confirmed knowledge cannot retain an agent policy version'
|
|
180
|
+
)
|
|
181
|
+
return
|
|
182
|
+
if basis.confirmed_by.kind != 'agent' or not basis.agent_policy_version:
|
|
183
|
+
raise ValueError(
|
|
184
|
+
'agent-confirmed knowledge requires an agent confirmer and policy version'
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _legacy_epistemic_status(
|
|
189
|
+
status: ConfirmationStatus,
|
|
190
|
+
quadrant: EpistemicQuadrant,
|
|
191
|
+
) -> EpistemicStatus:
|
|
192
|
+
if status == 'confirmed':
|
|
193
|
+
return 'confirmed'
|
|
194
|
+
return 'exploratory' if quadrant == 'unknown_unknown' else 'observed'
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _default_quadrant_reason(quadrant: EpistemicQuadrant) -> str:
|
|
198
|
+
return {
|
|
199
|
+
'known_known': 'The item was explicitly expressed when it was captured.',
|
|
200
|
+
'known_unknown': 'The item was captured as an explicit unresolved question.',
|
|
201
|
+
'unknown_known': 'The item was inferred from examples, behaviour, or reactions.',
|
|
202
|
+
'unknown_unknown': 'The item surfaced during blind-spot or open exploration.',
|
|
203
|
+
}[quadrant]
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class BootstrapRequest(StrictModel):
|
|
207
|
+
principal_name: str = Field(min_length=1, max_length=160)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class BootstrapResult(StrictModel):
|
|
211
|
+
principal_id: str
|
|
212
|
+
access_token: str
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class PrincipalCreate(StrictModel):
|
|
216
|
+
name: str = Field(min_length=1, max_length=160)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class PrincipalResult(StrictModel):
|
|
220
|
+
principal_id: str
|
|
221
|
+
access_token: str
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class ProjectSource(StrictModel):
|
|
225
|
+
key: str = Field(min_length=1, max_length=128)
|
|
226
|
+
kind: ProjectSourceKind
|
|
227
|
+
title: str = Field(min_length=1, max_length=512)
|
|
228
|
+
uri: str | None = Field(default=None, max_length=2048)
|
|
229
|
+
summary: str | None = Field(default=None, max_length=4096)
|
|
230
|
+
sensitivity: Sensitivity = 'normal'
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class AssessmentDimension(StrictModel):
|
|
234
|
+
key: str = Field(min_length=1, max_length=128)
|
|
235
|
+
label: str = Field(min_length=1, max_length=256)
|
|
236
|
+
score: int = Field(ge=0, le=100)
|
|
237
|
+
state: AssessmentState
|
|
238
|
+
evidence: list[str] = Field(default_factory=list, max_length=32)
|
|
239
|
+
|
|
240
|
+
@model_validator(mode='before')
|
|
241
|
+
@classmethod
|
|
242
|
+
def accept_legacy_missing_fields(cls, value):
|
|
243
|
+
if not isinstance(value, dict):
|
|
244
|
+
return value
|
|
245
|
+
compatible = dict(value)
|
|
246
|
+
compatible.pop('missing', None)
|
|
247
|
+
compatible.pop('recommendation', None)
|
|
248
|
+
if compatible.get('state') == 'missing':
|
|
249
|
+
compatible['state'] = 'inferred'
|
|
250
|
+
return compatible
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class ProjectAssessment(StrictModel):
|
|
254
|
+
score: int = Field(ge=0, le=100)
|
|
255
|
+
label: AssessmentLabel
|
|
256
|
+
confirmed: list[str] = Field(default_factory=list, max_length=64)
|
|
257
|
+
inferred: list[str] = Field(default_factory=list, max_length=64)
|
|
258
|
+
dimensions: list[AssessmentDimension] = Field(default_factory=list, max_length=16)
|
|
259
|
+
analyzed_at: datetime
|
|
260
|
+
|
|
261
|
+
@model_validator(mode='before')
|
|
262
|
+
@classmethod
|
|
263
|
+
def accept_legacy_missing_summary(cls, value):
|
|
264
|
+
if not isinstance(value, dict):
|
|
265
|
+
return value
|
|
266
|
+
compatible = dict(value)
|
|
267
|
+
compatible.pop('missing', None)
|
|
268
|
+
return compatible
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
class ProjectProfile(StrictModel):
|
|
272
|
+
name: str = Field(min_length=1, max_length=160)
|
|
273
|
+
purpose: str | None = Field(default=None, max_length=4096)
|
|
274
|
+
scope: str | None = Field(default=None, max_length=4096)
|
|
275
|
+
technical_summary: str | None = Field(default=None, max_length=4096)
|
|
276
|
+
lifecycle: ProjectLifecycle = 'planned'
|
|
277
|
+
sources: list[ProjectSource] = Field(default_factory=list, max_length=64)
|
|
278
|
+
boundaries: list[str] = Field(default_factory=list, max_length=64)
|
|
279
|
+
assessment: ProjectAssessment | None = None
|
|
280
|
+
|
|
281
|
+
@model_validator(mode='after')
|
|
282
|
+
def reject_credentials(self):
|
|
283
|
+
reject_credentials(self, 'Project profile')
|
|
284
|
+
return self
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class ProjectReleaseCreate(StrictModel):
|
|
288
|
+
version: str = Field(
|
|
289
|
+
min_length=1,
|
|
290
|
+
max_length=64,
|
|
291
|
+
pattern=r'^[0-9A-Za-z][0-9A-Za-z._-]*$',
|
|
292
|
+
)
|
|
293
|
+
summary: str = Field(min_length=1, max_length=4096)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class ProjectReleaseRecord(StrictModel):
|
|
297
|
+
id: str
|
|
298
|
+
project_id: str
|
|
299
|
+
version: str
|
|
300
|
+
summary: str
|
|
301
|
+
publisher_id: str
|
|
302
|
+
publisher_name: str
|
|
303
|
+
published_at: datetime
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class SpaceCreate(StrictModel):
|
|
307
|
+
name: str = Field(min_length=1, max_length=160)
|
|
308
|
+
kind: SpaceKind
|
|
309
|
+
description: str | None = Field(default=None, max_length=2000)
|
|
310
|
+
publication_key: str | None = Field(default=None, min_length=8, max_length=256)
|
|
311
|
+
profile: ProjectProfile | None = None
|
|
312
|
+
release: ProjectReleaseCreate | None = None
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
class SpaceRecord(StrictModel):
|
|
316
|
+
id: str
|
|
317
|
+
name: str
|
|
318
|
+
kind: SpaceKind
|
|
319
|
+
group_id: str
|
|
320
|
+
description: str | None
|
|
321
|
+
visibility: SpaceVisibility
|
|
322
|
+
owner_id: str
|
|
323
|
+
role: Role
|
|
324
|
+
publication_key: str | None = None
|
|
325
|
+
can_manage: bool = False
|
|
326
|
+
profile: ProjectProfile | None = None
|
|
327
|
+
current_release: ProjectReleaseRecord | None = None
|
|
328
|
+
created_at: datetime
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
class ProjectDeleteResult(StrictModel):
|
|
332
|
+
project_id: str
|
|
333
|
+
project_name: str
|
|
334
|
+
deleted: bool
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
class PersonalProjectUpsert(StrictModel):
|
|
338
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
339
|
+
project_id: str = Field(min_length=1, max_length=128)
|
|
340
|
+
profile: ProjectProfile
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
class PersonalProjectRecord(StrictModel):
|
|
344
|
+
project_id: str
|
|
345
|
+
personal_space_id: str
|
|
346
|
+
publication_key: str
|
|
347
|
+
profile: ProjectProfile
|
|
348
|
+
created_at: datetime
|
|
349
|
+
updated_at: datetime
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
class ProjectRelationCreate(StrictModel):
|
|
353
|
+
target_project_id: str = Field(min_length=1, max_length=128)
|
|
354
|
+
relation_type: ProjectRelationType
|
|
355
|
+
note: str | None = Field(default=None, max_length=2000)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
class ProjectRelationRecord(StrictModel):
|
|
359
|
+
id: str
|
|
360
|
+
source_project_id: str
|
|
361
|
+
target_project_id: str
|
|
362
|
+
relation_type: ProjectRelationType
|
|
363
|
+
status: ProjectRelationStatus
|
|
364
|
+
note: str | None
|
|
365
|
+
created_by: str
|
|
366
|
+
created_at: datetime
|
|
367
|
+
decided_by: str | None = None
|
|
368
|
+
decided_at: datetime | None = None
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
class ProjectRelationDecision(StrictModel):
|
|
372
|
+
decision: Literal['confirm', 'reject']
|
|
373
|
+
note: str | None = Field(default=None, max_length=2000)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
class MembershipCreate(StrictModel):
|
|
377
|
+
principal_id: str = Field(min_length=1, max_length=128)
|
|
378
|
+
role: Role
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
class MembershipRecord(StrictModel):
|
|
382
|
+
project_id: str
|
|
383
|
+
principal_id: str
|
|
384
|
+
role: Role
|
|
385
|
+
created_at: datetime
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
class SubscriptionCreate(StrictModel):
|
|
389
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
390
|
+
project_id: str = Field(min_length=1, max_length=128)
|
|
391
|
+
provider_url: str = Field(min_length=8, max_length=2048)
|
|
392
|
+
project_name: str = Field(min_length=1, max_length=160)
|
|
393
|
+
|
|
394
|
+
@field_validator('provider_url')
|
|
395
|
+
@classmethod
|
|
396
|
+
def validate_provider_url(cls, value: str) -> str:
|
|
397
|
+
return normalize_provider_url(value)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
class SubscriptionRecord(StrictModel):
|
|
401
|
+
id: str
|
|
402
|
+
personal_space_id: str
|
|
403
|
+
project_id: str
|
|
404
|
+
provider_url: str
|
|
405
|
+
project_name: str
|
|
406
|
+
created_at: datetime
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
class SubscriptionDeleteResult(StrictModel):
|
|
410
|
+
project_id: str
|
|
411
|
+
deleted: bool
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
class EntityInput(StrictModel):
|
|
415
|
+
key: str = Field(min_length=1, max_length=256)
|
|
416
|
+
name: str = Field(min_length=1, max_length=512)
|
|
417
|
+
type: str = Field(min_length=1, max_length=64, pattern=r'^[A-Za-z][A-Za-z0-9_]*$')
|
|
418
|
+
summary: str = Field(default='', max_length=4096)
|
|
419
|
+
origin_quadrant: EpistemicQuadrant = 'known_known'
|
|
420
|
+
current_quadrant: EpistemicQuadrant | None = None
|
|
421
|
+
epistemic_status: EpistemicStatus = 'confirmed'
|
|
422
|
+
confirmation_status: ConfirmationStatus | None = None
|
|
423
|
+
confirmation_basis: ConfirmationBasis | None = None
|
|
424
|
+
reasoning_summary: str | None = Field(default=None, max_length=4096)
|
|
425
|
+
profile_aspect: PersonalProfileAspect | None = None
|
|
426
|
+
inheritance_mode: KnowledgeInheritanceMode = 'local_only'
|
|
427
|
+
inherited_project_ids: list[str] = Field(default_factory=list, max_length=32)
|
|
428
|
+
attributes: dict[str, Any] = Field(default_factory=dict)
|
|
429
|
+
|
|
430
|
+
@field_validator('attributes')
|
|
431
|
+
@classmethod
|
|
432
|
+
def bound_attributes(cls, value: dict[str, Any]) -> dict[str, Any]:
|
|
433
|
+
if len(value) > 32:
|
|
434
|
+
raise ValueError('an entity may contain at most 32 attributes')
|
|
435
|
+
return value
|
|
436
|
+
|
|
437
|
+
@model_validator(mode='after')
|
|
438
|
+
def validate_inheritance(self):
|
|
439
|
+
_validate_inheritance(
|
|
440
|
+
self.inheritance_mode,
|
|
441
|
+
self.inherited_project_ids,
|
|
442
|
+
self.profile_aspect,
|
|
443
|
+
)
|
|
444
|
+
return self
|
|
445
|
+
|
|
446
|
+
@model_validator(mode='after')
|
|
447
|
+
def complete_epistemic_state(self):
|
|
448
|
+
self.current_quadrant = complete_epistemic_state(
|
|
449
|
+
origin_quadrant=self.origin_quadrant,
|
|
450
|
+
current_quadrant=self.current_quadrant,
|
|
451
|
+
epistemic_status=self.epistemic_status,
|
|
452
|
+
reasoning_summary=self.reasoning_summary,
|
|
453
|
+
profile_aspect=self.profile_aspect,
|
|
454
|
+
)
|
|
455
|
+
return self
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
class RelationshipInput(StrictModel):
|
|
459
|
+
key: str = Field(min_length=1, max_length=256)
|
|
460
|
+
source: str = Field(min_length=1, max_length=256)
|
|
461
|
+
target: str = Field(min_length=1, max_length=256)
|
|
462
|
+
type: str = Field(min_length=1, max_length=64, pattern=r'^[A-Z][A-Z0-9_]*$')
|
|
463
|
+
fact: str = Field(min_length=1, max_length=8192)
|
|
464
|
+
valid_at: datetime | None = None
|
|
465
|
+
invalid_at: datetime | None = None
|
|
466
|
+
supersedes: list[str] = Field(default_factory=list, max_length=32)
|
|
467
|
+
confidence: float = Field(default=1, ge=0, le=1)
|
|
468
|
+
origin_quadrant: EpistemicQuadrant = 'known_known'
|
|
469
|
+
current_quadrant: EpistemicQuadrant | None = None
|
|
470
|
+
epistemic_status: EpistemicStatus = 'confirmed'
|
|
471
|
+
confirmation_status: ConfirmationStatus | None = None
|
|
472
|
+
confirmation_basis: ConfirmationBasis | None = None
|
|
473
|
+
reasoning_summary: str | None = Field(default=None, max_length=4096)
|
|
474
|
+
profile_aspect: PersonalProfileAspect | None = None
|
|
475
|
+
inheritance_mode: KnowledgeInheritanceMode = 'local_only'
|
|
476
|
+
inherited_project_ids: list[str] = Field(default_factory=list, max_length=32)
|
|
477
|
+
attributes: dict[str, Any] = Field(default_factory=dict)
|
|
478
|
+
|
|
479
|
+
@model_validator(mode='after')
|
|
480
|
+
def validate_interval(self):
|
|
481
|
+
if self.valid_at and self.invalid_at and self.invalid_at < self.valid_at:
|
|
482
|
+
raise ValueError('invalid_at must not be earlier than valid_at')
|
|
483
|
+
self.current_quadrant = complete_epistemic_state(
|
|
484
|
+
origin_quadrant=self.origin_quadrant,
|
|
485
|
+
current_quadrant=self.current_quadrant,
|
|
486
|
+
epistemic_status=self.epistemic_status,
|
|
487
|
+
reasoning_summary=self.reasoning_summary,
|
|
488
|
+
profile_aspect=self.profile_aspect,
|
|
489
|
+
)
|
|
490
|
+
_validate_inheritance(
|
|
491
|
+
self.inheritance_mode,
|
|
492
|
+
self.inherited_project_ids,
|
|
493
|
+
self.profile_aspect,
|
|
494
|
+
)
|
|
495
|
+
return self
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
class StructuredEpisode(StrictModel):
|
|
499
|
+
idempotency_key: str = Field(min_length=8, max_length=256)
|
|
500
|
+
session_id: str = Field(min_length=1, max_length=256)
|
|
501
|
+
name: str = Field(min_length=1, max_length=512)
|
|
502
|
+
source_kind: str = Field(min_length=1, max_length=128)
|
|
503
|
+
source_description: str = Field(min_length=1, max_length=1024)
|
|
504
|
+
source_application: SourceApplication | None = None
|
|
505
|
+
source_turn_id: str | None = Field(default=None, min_length=1, max_length=256)
|
|
506
|
+
source_excerpt: str | None = Field(default=None, min_length=1, max_length=2048)
|
|
507
|
+
reference_time: datetime
|
|
508
|
+
summary: str = Field(default='', max_length=8192)
|
|
509
|
+
sensitivity: Sensitivity = 'normal'
|
|
510
|
+
entities: list[EntityInput] = Field(min_length=1, max_length=128)
|
|
511
|
+
relationships: list[RelationshipInput] = Field(default_factory=list, max_length=256)
|
|
512
|
+
|
|
513
|
+
@model_validator(mode='after')
|
|
514
|
+
def complete_confirmation_state(self):
|
|
515
|
+
proposer = ConfirmationActor(
|
|
516
|
+
kind='agent' if self.source_application else 'import',
|
|
517
|
+
label=self.source_application or self.source_kind,
|
|
518
|
+
)
|
|
519
|
+
for item in [*self.entities, *self.relationships]:
|
|
520
|
+
if item.confirmation_basis is None:
|
|
521
|
+
item.confirmation_basis = ConfirmationBasis(
|
|
522
|
+
existence_reason=self.source_description,
|
|
523
|
+
quadrant_reason=(
|
|
524
|
+
item.reasoning_summary
|
|
525
|
+
or _default_quadrant_reason(item.origin_quadrant)
|
|
526
|
+
),
|
|
527
|
+
proposed_by=proposer,
|
|
528
|
+
)
|
|
529
|
+
if item.confirmation_status is None:
|
|
530
|
+
# Legacy epistemic_status is evidence maturity, not an auditable
|
|
531
|
+
# confirmation. Old records must be reviewed instead of silently
|
|
532
|
+
# inheriting "confirmed".
|
|
533
|
+
item.confirmation_status = 'pending'
|
|
534
|
+
_validate_confirmation_state(
|
|
535
|
+
item.confirmation_status,
|
|
536
|
+
item.confirmation_basis,
|
|
537
|
+
)
|
|
538
|
+
item.epistemic_status = _legacy_epistemic_status(
|
|
539
|
+
item.confirmation_status,
|
|
540
|
+
item.origin_quadrant,
|
|
541
|
+
)
|
|
542
|
+
return self
|
|
543
|
+
|
|
544
|
+
@model_validator(mode="after")
|
|
545
|
+
def reject_credentials(self) -> "StructuredEpisode":
|
|
546
|
+
reject_credentials(self, 'Structured episode')
|
|
547
|
+
return self
|
|
548
|
+
|
|
549
|
+
@model_validator(mode='after')
|
|
550
|
+
def validate_graph_references(self):
|
|
551
|
+
entity_keys = [entity.key for entity in self.entities]
|
|
552
|
+
if len(entity_keys) != len(set(entity_keys)):
|
|
553
|
+
raise ValueError('entity keys must be unique within an episode')
|
|
554
|
+
relationship_keys = [relationship.key for relationship in self.relationships]
|
|
555
|
+
if len(relationship_keys) != len(set(relationship_keys)):
|
|
556
|
+
raise ValueError('relationship keys must be unique within an episode')
|
|
557
|
+
available = set(entity_keys)
|
|
558
|
+
superseded_keys: list[str] = []
|
|
559
|
+
for relationship in self.relationships:
|
|
560
|
+
if relationship.source not in available or relationship.target not in available:
|
|
561
|
+
raise ValueError('relationship source and target must reference episode entities')
|
|
562
|
+
if relationship.invalid_at and relationship.supersedes:
|
|
563
|
+
raise ValueError('an invalid relationship cannot replace existing knowledge')
|
|
564
|
+
superseded_keys.extend(relationship.supersedes)
|
|
565
|
+
if len(superseded_keys) != len(set(superseded_keys)):
|
|
566
|
+
raise ValueError(
|
|
567
|
+
'one existing relationship cannot have multiple replacements in an episode'
|
|
568
|
+
)
|
|
569
|
+
return self
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
class PublicationDraftCreate(StrictModel):
|
|
573
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
574
|
+
target_project_id: str = Field(min_length=1, max_length=128)
|
|
575
|
+
provider_url: str = Field(min_length=8, max_length=2048)
|
|
576
|
+
episode: StructuredEpisode
|
|
577
|
+
|
|
578
|
+
@field_validator('provider_url')
|
|
579
|
+
@classmethod
|
|
580
|
+
def validate_provider_url(cls, value: str) -> str:
|
|
581
|
+
return normalize_provider_url(value)
|
|
582
|
+
|
|
583
|
+
@model_validator(mode='after')
|
|
584
|
+
def require_public_eligible_episode(self):
|
|
585
|
+
require_public_eligible_episode(self.episode)
|
|
586
|
+
return self
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
class PublicationDraftRecord(StrictModel):
|
|
590
|
+
id: str
|
|
591
|
+
personal_space_id: str
|
|
592
|
+
target_project_id: str
|
|
593
|
+
provider_url: str
|
|
594
|
+
status: Literal['pending', 'submitted', 'kept_personal', 'ignored']
|
|
595
|
+
episode: StructuredEpisode
|
|
596
|
+
created_at: datetime
|
|
597
|
+
decided_at: datetime | None = None
|
|
598
|
+
shared_proposal_id: str | None = None
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
class PublicationDraftDecision(StrictModel):
|
|
602
|
+
decision: Literal['submit_public', 'keep_personal', 'ignore']
|
|
603
|
+
shared_proposal_id: str | None = Field(default=None, max_length=128)
|
|
604
|
+
|
|
605
|
+
@model_validator(mode='after')
|
|
606
|
+
def require_shared_proposal(self):
|
|
607
|
+
if self.decision == 'submit_public' and not self.shared_proposal_id:
|
|
608
|
+
raise ValueError('shared_proposal_id is required after public submission')
|
|
609
|
+
if self.decision != 'submit_public' and self.shared_proposal_id:
|
|
610
|
+
raise ValueError('shared_proposal_id is only valid for public submission')
|
|
611
|
+
return self
|
|
612
|
+
|
|
613
|
+
class KnowledgeCommit(StrictModel):
|
|
614
|
+
space_id: str = Field(min_length=1, max_length=128)
|
|
615
|
+
personal_project_id: str | None = Field(default=None, min_length=1, max_length=128)
|
|
616
|
+
episode: StructuredEpisode
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
class CommitResult(StrictModel):
|
|
620
|
+
status: Literal['committed', 'duplicate']
|
|
621
|
+
space_id: str
|
|
622
|
+
episode_id: str
|
|
623
|
+
entity_ids: list[str]
|
|
624
|
+
relationship_ids: list[str]
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
class ProposalCreate(StrictModel):
|
|
628
|
+
episode: StructuredEpisode
|
|
629
|
+
|
|
630
|
+
@model_validator(mode='after')
|
|
631
|
+
def require_public_eligible_episode(self):
|
|
632
|
+
require_public_eligible_episode(self.episode)
|
|
633
|
+
return self
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
class ProposalRecord(StrictModel):
|
|
637
|
+
id: str
|
|
638
|
+
project_id: str
|
|
639
|
+
submitted_by: str
|
|
640
|
+
status: Literal['pending', 'processing', 'approved', 'rejected']
|
|
641
|
+
episode: StructuredEpisode
|
|
642
|
+
created_at: datetime
|
|
643
|
+
decided_at: datetime | None = None
|
|
644
|
+
decided_by: str | None = None
|
|
645
|
+
decision_note: str | None = None
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
class ProposalDecision(StrictModel):
|
|
649
|
+
decision: Literal['approve', 'reject']
|
|
650
|
+
note: str | None = Field(default=None, max_length=2000)
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
class SearchRequest(StrictModel):
|
|
654
|
+
space_ids: list[str] = Field(min_length=1, max_length=32)
|
|
655
|
+
query: str = Field(min_length=1, max_length=2048)
|
|
656
|
+
limit: int = Field(default=12, ge=1, le=100)
|
|
657
|
+
include_historical: bool = False
|
|
658
|
+
include_pending: bool = Field(
|
|
659
|
+
default=False,
|
|
660
|
+
validation_alias=AliasChoices('include_pending', 'include_exploratory'),
|
|
661
|
+
)
|
|
662
|
+
personal_project_ids: list[str] = Field(default_factory=list, max_length=16)
|
|
663
|
+
active_personal_project_id: str | None = Field(
|
|
664
|
+
default=None,
|
|
665
|
+
min_length=1,
|
|
666
|
+
max_length=128,
|
|
667
|
+
)
|
|
668
|
+
inherit_project_knowledge: bool = True
|
|
669
|
+
include_personal_global: bool = False
|
|
670
|
+
|
|
671
|
+
@field_validator('personal_project_ids')
|
|
672
|
+
@classmethod
|
|
673
|
+
def validate_personal_project_ids(cls, value: list[str]) -> list[str]:
|
|
674
|
+
normalized = [item.strip() for item in value]
|
|
675
|
+
if any(not item or len(item) > 128 for item in normalized):
|
|
676
|
+
raise ValueError('personal project ids must contain 1 to 128 characters')
|
|
677
|
+
if len(set(normalized)) != len(normalized):
|
|
678
|
+
raise ValueError('personal project ids must be unique')
|
|
679
|
+
return normalized
|
|
680
|
+
|
|
681
|
+
@model_validator(mode='after')
|
|
682
|
+
def validate_active_personal_project(self):
|
|
683
|
+
if (
|
|
684
|
+
self.active_personal_project_id
|
|
685
|
+
and self.active_personal_project_id not in self.personal_project_ids
|
|
686
|
+
):
|
|
687
|
+
raise ValueError(
|
|
688
|
+
'active personal project id must be present in personal project ids'
|
|
689
|
+
)
|
|
690
|
+
return self
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
class EntitySearchResult(StrictModel):
|
|
694
|
+
id: str
|
|
695
|
+
space_id: str
|
|
696
|
+
group_id: str
|
|
697
|
+
name: str
|
|
698
|
+
type: str
|
|
699
|
+
summary: str
|
|
700
|
+
created_at: datetime | None = None
|
|
701
|
+
origin_quadrant: EpistemicQuadrant = 'known_known'
|
|
702
|
+
current_quadrant: EpistemicQuadrant = 'known_known'
|
|
703
|
+
epistemic_status: EpistemicStatus = 'confirmed'
|
|
704
|
+
confirmation_status: ConfirmationStatus = 'pending'
|
|
705
|
+
confirmation_basis: ConfirmationBasis | None = None
|
|
706
|
+
reasoning_summary: str | None = None
|
|
707
|
+
profile_aspect: PersonalProfileAspect | None = None
|
|
708
|
+
preference_scope: PreferenceScope | None = None
|
|
709
|
+
preference_project_id: str | None = None
|
|
710
|
+
key: str | None = None
|
|
711
|
+
defined_project_id: str | None = None
|
|
712
|
+
inheritance_mode: KnowledgeInheritanceMode = 'local_only'
|
|
713
|
+
inherited_project_ids: list[str] = Field(default_factory=list, max_length=32)
|
|
714
|
+
human_edited: bool = False
|
|
715
|
+
human_change_status: HumanChangeStatus = 'none'
|
|
716
|
+
human_change_version: int = Field(default=0, ge=0)
|
|
717
|
+
last_human_changed_at: datetime | None = None
|
|
718
|
+
last_agent_viewed_at: datetime | None = None
|
|
719
|
+
last_agent_reviewed_at: datetime | None = None
|
|
720
|
+
utility_score: float = Field(default=0, ge=0, le=1)
|
|
721
|
+
confidence_score: float = Field(default=0.5, ge=0, le=1)
|
|
722
|
+
qualified_use_count: int = Field(default=0, ge=0)
|
|
723
|
+
distinct_task_count: int = Field(default=0, ge=0)
|
|
724
|
+
last_used_at: datetime | None = None
|
|
725
|
+
scope_distance: int = Field(default=0, ge=0, le=8)
|
|
726
|
+
inherited_from_project_id: str | None = None
|
|
727
|
+
scope_path: list[str] = Field(default_factory=list, max_length=9)
|
|
728
|
+
score: float | None = None
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
class FactResult(StrictModel):
|
|
732
|
+
id: str
|
|
733
|
+
space_id: str
|
|
734
|
+
group_id: str
|
|
735
|
+
source_entity: str
|
|
736
|
+
target_entity: str
|
|
737
|
+
relationship: str
|
|
738
|
+
fact: str
|
|
739
|
+
valid_at: datetime | None
|
|
740
|
+
invalid_at: datetime | None
|
|
741
|
+
created_at: datetime
|
|
742
|
+
episodes: list[str]
|
|
743
|
+
origin_quadrant: EpistemicQuadrant = 'known_known'
|
|
744
|
+
current_quadrant: EpistemicQuadrant = 'known_known'
|
|
745
|
+
epistemic_status: EpistemicStatus = 'confirmed'
|
|
746
|
+
confirmation_status: ConfirmationStatus = 'pending'
|
|
747
|
+
confirmation_basis: ConfirmationBasis | None = None
|
|
748
|
+
reasoning_summary: str | None = None
|
|
749
|
+
profile_aspect: PersonalProfileAspect | None = None
|
|
750
|
+
preference_scope: PreferenceScope | None = None
|
|
751
|
+
preference_project_id: str | None = None
|
|
752
|
+
key: str | None = None
|
|
753
|
+
defined_project_id: str | None = None
|
|
754
|
+
inheritance_mode: KnowledgeInheritanceMode = 'local_only'
|
|
755
|
+
inherited_project_ids: list[str] = Field(default_factory=list, max_length=32)
|
|
756
|
+
human_edited: bool = False
|
|
757
|
+
human_change_status: HumanChangeStatus = 'none'
|
|
758
|
+
human_change_version: int = Field(default=0, ge=0)
|
|
759
|
+
last_human_changed_at: datetime | None = None
|
|
760
|
+
last_agent_viewed_at: datetime | None = None
|
|
761
|
+
last_agent_reviewed_at: datetime | None = None
|
|
762
|
+
utility_score: float = Field(default=0, ge=0, le=1)
|
|
763
|
+
confidence_score: float = Field(default=0.5, ge=0, le=1)
|
|
764
|
+
qualified_use_count: int = Field(default=0, ge=0)
|
|
765
|
+
distinct_task_count: int = Field(default=0, ge=0)
|
|
766
|
+
last_used_at: datetime | None = None
|
|
767
|
+
scope_distance: int = Field(default=0, ge=0, le=8)
|
|
768
|
+
inherited_from_project_id: str | None = None
|
|
769
|
+
scope_path: list[str] = Field(default_factory=list, max_length=9)
|
|
770
|
+
score: float | None = None
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
class SearchResult(StrictModel):
|
|
774
|
+
facts: list[FactResult]
|
|
775
|
+
entities: list[EntitySearchResult] = Field(default_factory=list)
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
class CollaborationPreferenceItem(StrictModel):
|
|
779
|
+
id: str
|
|
780
|
+
item_kind: KnowledgeItemKind
|
|
781
|
+
key: str
|
|
782
|
+
preference_key: str
|
|
783
|
+
title: str
|
|
784
|
+
instruction: str
|
|
785
|
+
profile_aspect: PersonalProfileAspect
|
|
786
|
+
preference_scope: PreferenceScope
|
|
787
|
+
preference_project_id: str | None = None
|
|
788
|
+
attributes: dict[str, Any] = Field(default_factory=dict)
|
|
789
|
+
confirmation_status: Literal['confirmed', 'agent_confirmed']
|
|
790
|
+
confirmed_at: datetime
|
|
791
|
+
created_at: datetime | None = None
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
class CollaborationPreferenceConflict(StrictModel):
|
|
795
|
+
preference_key: str
|
|
796
|
+
preference_scope: PreferenceScope
|
|
797
|
+
preference_project_id: str | None = None
|
|
798
|
+
item_ids: list[str] = Field(min_length=2)
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
class CollaborationContextResult(StrictModel):
|
|
802
|
+
personal_space_id: str
|
|
803
|
+
personal_project_id: str | None = None
|
|
804
|
+
global_preferences: list[CollaborationPreferenceItem] = Field(default_factory=list)
|
|
805
|
+
project_preferences: list[CollaborationPreferenceItem] = Field(default_factory=list)
|
|
806
|
+
effective_preferences: list[CollaborationPreferenceItem] = Field(default_factory=list)
|
|
807
|
+
conflicts: list[CollaborationPreferenceConflict] = Field(default_factory=list)
|
|
808
|
+
overridden_global_ids: list[str] = Field(default_factory=list)
|
|
809
|
+
truncated: bool = False
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
class PreferenceConflictDeferCreate(StrictModel):
|
|
813
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
814
|
+
conflict_id: str = Field(min_length=1, max_length=1024)
|
|
815
|
+
preference_key: str = Field(min_length=1, max_length=512)
|
|
816
|
+
preference_scope: PreferenceScope
|
|
817
|
+
preference_project_id: str | None = Field(default=None, min_length=1, max_length=128)
|
|
818
|
+
left_item_id: str = Field(min_length=1, max_length=256)
|
|
819
|
+
left_item_kind: KnowledgeItemKind
|
|
820
|
+
right_item_id: str = Field(min_length=1, max_length=256)
|
|
821
|
+
right_item_kind: KnowledgeItemKind
|
|
822
|
+
reason: str = Field(min_length=1, max_length=2000)
|
|
823
|
+
operation_actor: KnowledgeOperationActor = 'human'
|
|
824
|
+
|
|
825
|
+
@model_validator(mode='after')
|
|
826
|
+
def validate_conflict(self):
|
|
827
|
+
if (
|
|
828
|
+
self.left_item_id == self.right_item_id
|
|
829
|
+
and self.left_item_kind == self.right_item_kind
|
|
830
|
+
):
|
|
831
|
+
raise ValueError('preference conflict requires two different items')
|
|
832
|
+
if self.preference_scope == 'project' and not self.preference_project_id:
|
|
833
|
+
raise ValueError('project preference conflict requires preference_project_id')
|
|
834
|
+
if self.preference_scope == 'global' and self.preference_project_id:
|
|
835
|
+
raise ValueError('global preference conflict cannot set preference_project_id')
|
|
836
|
+
return self
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
class PreferenceConflictResolveCreate(StrictModel):
|
|
840
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
841
|
+
resolution: PreferenceConflictResolutionAction
|
|
842
|
+
reason: str = Field(min_length=1, max_length=2000)
|
|
843
|
+
canonical_item_id: str | None = Field(default=None, min_length=1, max_length=256)
|
|
844
|
+
merged_instruction: str | None = Field(default=None, min_length=1, max_length=4096)
|
|
845
|
+
split_item_id: str | None = Field(default=None, min_length=1, max_length=256)
|
|
846
|
+
split_project_id: str | None = Field(default=None, min_length=1, max_length=128)
|
|
847
|
+
operation_actor: KnowledgeOperationActor = 'agent'
|
|
848
|
+
|
|
849
|
+
@model_validator(mode='after')
|
|
850
|
+
def validate_resolution_details(self):
|
|
851
|
+
if self.resolution == 'merge':
|
|
852
|
+
if not self.canonical_item_id or not self.merged_instruction:
|
|
853
|
+
raise ValueError(
|
|
854
|
+
'merge requires canonical_item_id and merged_instruction'
|
|
855
|
+
)
|
|
856
|
+
if self.resolution == 'split_scope':
|
|
857
|
+
if not self.split_item_id or not self.split_project_id:
|
|
858
|
+
raise ValueError('split_scope requires split_item_id and split_project_id')
|
|
859
|
+
return self
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
class PreferenceConflictCompleteCreate(StrictModel):
|
|
863
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
864
|
+
resolution: PreferenceConflictResolutionAction
|
|
865
|
+
reason: str = Field(min_length=1, max_length=2000)
|
|
866
|
+
operation_actor: KnowledgeOperationActor = 'human'
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
class PreferenceConflictRecord(StrictModel):
|
|
870
|
+
id: str
|
|
871
|
+
personal_space_id: str
|
|
872
|
+
preference_key: str
|
|
873
|
+
preference_scope: PreferenceScope
|
|
874
|
+
preference_project_id: str | None = None
|
|
875
|
+
left_item_id: str
|
|
876
|
+
left_item_kind: KnowledgeItemKind
|
|
877
|
+
right_item_id: str
|
|
878
|
+
right_item_kind: KnowledgeItemKind
|
|
879
|
+
status: PreferenceConflictQueueStatus
|
|
880
|
+
requested_by: KnowledgeOperationActor
|
|
881
|
+
resolution: PreferenceConflictResolutionAction | None = None
|
|
882
|
+
resolved_by: KnowledgeOperationActor | None = None
|
|
883
|
+
reason: str
|
|
884
|
+
resolution_reason: str | None = None
|
|
885
|
+
deferred_at: datetime
|
|
886
|
+
resolved_at: datetime | None = None
|
|
887
|
+
updated_at: datetime
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
class GraphEvidence(StrictModel):
|
|
891
|
+
id: str
|
|
892
|
+
name: str
|
|
893
|
+
source_description: str
|
|
894
|
+
source_kind: str
|
|
895
|
+
summary: str
|
|
896
|
+
session_id: str | None = None
|
|
897
|
+
source_application: SourceApplication | None = None
|
|
898
|
+
source_turn_id: str | None = None
|
|
899
|
+
source_excerpt: str | None = None
|
|
900
|
+
personal_project_id: str | None = None
|
|
901
|
+
reference_time: datetime | None = None
|
|
902
|
+
created_at: datetime | None = None
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
class KnowledgeRevisionCreate(StrictModel):
|
|
906
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
907
|
+
personal_project_id: str | None = Field(default=None, min_length=1, max_length=128)
|
|
908
|
+
item_kind: KnowledgeItemKind
|
|
909
|
+
action: KnowledgeContentRevisionAction
|
|
910
|
+
reason: str = Field(min_length=1, max_length=2000)
|
|
911
|
+
name: str | None = Field(default=None, min_length=1, max_length=512)
|
|
912
|
+
summary: str | None = Field(default=None, max_length=4096)
|
|
913
|
+
fact: str | None = Field(default=None, min_length=1, max_length=8192)
|
|
914
|
+
origin_quadrant: EpistemicQuadrant | None = None
|
|
915
|
+
current_quadrant: EpistemicQuadrant | None = None
|
|
916
|
+
epistemic_status: EpistemicStatus | None = None
|
|
917
|
+
confirmation_status: ConfirmationStatus | None = None
|
|
918
|
+
confirmation_basis: ConfirmationBasis | None = None
|
|
919
|
+
reasoning_summary: str | None = Field(default=None, max_length=4096)
|
|
920
|
+
profile_aspect: PersonalProfileAspect | Literal['none'] | None = None
|
|
921
|
+
inheritance_mode: KnowledgeInheritanceMode | None = None
|
|
922
|
+
inherited_project_ids: list[str] | None = Field(default=None, max_length=32)
|
|
923
|
+
replacement_item_id: str | None = Field(default=None, min_length=1, max_length=256)
|
|
924
|
+
replacement_item_kind: KnowledgeItemKind | None = None
|
|
925
|
+
operation_actor: KnowledgeOperationActor = 'agent'
|
|
926
|
+
|
|
927
|
+
@model_validator(mode='after')
|
|
928
|
+
def validate_update_fields(self):
|
|
929
|
+
has_replacement_id = self.replacement_item_id is not None
|
|
930
|
+
has_replacement_kind = self.replacement_item_kind is not None
|
|
931
|
+
if has_replacement_id != has_replacement_kind:
|
|
932
|
+
raise ValueError(
|
|
933
|
+
'replacement item id and kind must be provided together'
|
|
934
|
+
)
|
|
935
|
+
if self.action == 'link_replacement' and not has_replacement_id:
|
|
936
|
+
raise ValueError('linking a replacement requires a replacement item')
|
|
937
|
+
if (
|
|
938
|
+
self.action not in {'invalidate', 'link_replacement'}
|
|
939
|
+
and has_replacement_id
|
|
940
|
+
):
|
|
941
|
+
raise ValueError(
|
|
942
|
+
'replacement item is only valid when invalidating or linking a replacement'
|
|
943
|
+
)
|
|
944
|
+
if self.action == 'confirm':
|
|
945
|
+
if (
|
|
946
|
+
self.confirmation_status != 'confirmed'
|
|
947
|
+
or self.confirmation_basis is None
|
|
948
|
+
):
|
|
949
|
+
raise ValueError(
|
|
950
|
+
'confirmation requires confirmed status and a structured basis'
|
|
951
|
+
)
|
|
952
|
+
if any(value is not None for value in (
|
|
953
|
+
self.name,
|
|
954
|
+
self.summary,
|
|
955
|
+
self.fact,
|
|
956
|
+
self.origin_quadrant,
|
|
957
|
+
self.current_quadrant,
|
|
958
|
+
self.epistemic_status,
|
|
959
|
+
self.reasoning_summary,
|
|
960
|
+
self.profile_aspect,
|
|
961
|
+
self.inheritance_mode,
|
|
962
|
+
self.inherited_project_ids,
|
|
963
|
+
)):
|
|
964
|
+
raise ValueError('confirmation cannot change knowledge content or taxonomy')
|
|
965
|
+
if self.action == 'update':
|
|
966
|
+
taxonomy_changed = any(value is not None for value in (
|
|
967
|
+
self.current_quadrant,
|
|
968
|
+
self.epistemic_status,
|
|
969
|
+
self.origin_quadrant,
|
|
970
|
+
self.confirmation_status,
|
|
971
|
+
self.confirmation_basis,
|
|
972
|
+
self.reasoning_summary,
|
|
973
|
+
self.profile_aspect,
|
|
974
|
+
self.inheritance_mode,
|
|
975
|
+
self.inherited_project_ids,
|
|
976
|
+
))
|
|
977
|
+
if (
|
|
978
|
+
self.item_kind == 'entity'
|
|
979
|
+
and self.name is None
|
|
980
|
+
and self.summary is None
|
|
981
|
+
and not taxonomy_changed
|
|
982
|
+
):
|
|
983
|
+
raise ValueError('entity update requires content or epistemic metadata')
|
|
984
|
+
if self.item_kind == 'relationship' and self.fact is None and not taxonomy_changed:
|
|
985
|
+
raise ValueError('relationship update requires content or epistemic metadata')
|
|
986
|
+
if self.confirmation_status is not None:
|
|
987
|
+
if self.confirmation_status == 'agent_confirmed':
|
|
988
|
+
raise ValueError(
|
|
989
|
+
'agent-confirmed status can only be produced by the '
|
|
990
|
+
'knowledge usage policy'
|
|
991
|
+
)
|
|
992
|
+
if self.confirmation_basis is None:
|
|
993
|
+
raise ValueError('confirmation status requires a structured confirmation basis')
|
|
994
|
+
_validate_confirmation_state(
|
|
995
|
+
self.confirmation_status,
|
|
996
|
+
self.confirmation_basis,
|
|
997
|
+
)
|
|
998
|
+
if (self.inheritance_mode is None) != (self.inherited_project_ids is None):
|
|
999
|
+
raise ValueError(
|
|
1000
|
+
'inheritance mode and inherited project ids must be updated together'
|
|
1001
|
+
)
|
|
1002
|
+
if self.inheritance_mode is not None:
|
|
1003
|
+
_validate_inheritance(
|
|
1004
|
+
self.inheritance_mode,
|
|
1005
|
+
self.inherited_project_ids,
|
|
1006
|
+
None if self.profile_aspect in {None, 'none'} else self.profile_aspect,
|
|
1007
|
+
)
|
|
1008
|
+
reject_credentials(self, 'Knowledge revision')
|
|
1009
|
+
return self
|
|
1010
|
+
|
|
1011
|
+
|
|
1012
|
+
class KnowledgeBatchConfirmationItem(StrictModel):
|
|
1013
|
+
item_id: str = Field(min_length=1, max_length=256)
|
|
1014
|
+
item_kind: KnowledgeItemKind
|
|
1015
|
+
existence_reason: str = Field(min_length=1, max_length=4096)
|
|
1016
|
+
quadrant_reason: str = Field(min_length=1, max_length=4096)
|
|
1017
|
+
proposed_by: ConfirmationActor
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
class KnowledgeBatchConfirmationCreate(StrictModel):
|
|
1021
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
1022
|
+
group_kind: KnowledgeConfirmationGroupKind
|
|
1023
|
+
group_value: str = Field(min_length=1, max_length=256)
|
|
1024
|
+
reason: str = Field(min_length=1, max_length=2000)
|
|
1025
|
+
confirmer: ConfirmationActor
|
|
1026
|
+
items: list[KnowledgeBatchConfirmationItem] = Field(
|
|
1027
|
+
min_length=2,
|
|
1028
|
+
max_length=200,
|
|
1029
|
+
)
|
|
1030
|
+
operation_actor: KnowledgeOperationActor = 'agent'
|
|
1031
|
+
|
|
1032
|
+
@model_validator(mode='after')
|
|
1033
|
+
def validate_batch_confirmation(self):
|
|
1034
|
+
if self.confirmer.kind not in {'user', 'authoritative_source'}:
|
|
1035
|
+
raise ValueError('an agent or import cannot confirm knowledge')
|
|
1036
|
+
if self.confirmer.kind == 'authoritative_source' and not self.confirmer.label:
|
|
1037
|
+
raise ValueError('an authoritative source confirmer requires a label')
|
|
1038
|
+
identities = {
|
|
1039
|
+
(item.item_kind, item.item_id)
|
|
1040
|
+
for item in self.items
|
|
1041
|
+
}
|
|
1042
|
+
if len(identities) != len(self.items):
|
|
1043
|
+
raise ValueError('batch confirmation items must be unique')
|
|
1044
|
+
reject_credentials(self, 'Knowledge batch confirmation')
|
|
1045
|
+
return self
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
class KnowledgeBatchConfirmationResult(StrictModel):
|
|
1049
|
+
group_kind: KnowledgeConfirmationGroupKind
|
|
1050
|
+
group_value: str
|
|
1051
|
+
confirmed_count: int = Field(ge=0, le=200)
|
|
1052
|
+
confirmed_at: datetime
|
|
1053
|
+
item_keys: list[str] = Field(max_length=200)
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
class KnowledgeAssignmentChange(StrictModel):
|
|
1057
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
1058
|
+
item_kind: KnowledgeItemKind
|
|
1059
|
+
target_project_id: str = Field(min_length=1, max_length=128)
|
|
1060
|
+
reason: str = Field(min_length=1, max_length=2000)
|
|
1061
|
+
operation_actor: KnowledgeOperationActor = 'agent'
|
|
1062
|
+
|
|
1063
|
+
|
|
1064
|
+
class PreferenceScopeChange(StrictModel):
|
|
1065
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
1066
|
+
item_kind: KnowledgeItemKind
|
|
1067
|
+
scope: PreferenceScope
|
|
1068
|
+
project_id: str | None = Field(default=None, min_length=1, max_length=128)
|
|
1069
|
+
reason: str = Field(min_length=1, max_length=2000)
|
|
1070
|
+
operation_actor: KnowledgeOperationActor = 'agent'
|
|
1071
|
+
|
|
1072
|
+
@model_validator(mode='after')
|
|
1073
|
+
def validate_project_scope(self):
|
|
1074
|
+
if self.scope == 'project' and not self.project_id:
|
|
1075
|
+
raise ValueError('project_id is required for project preference scope')
|
|
1076
|
+
if self.scope == 'global' and self.project_id:
|
|
1077
|
+
raise ValueError('project_id is only valid for project preference scope')
|
|
1078
|
+
return self
|
|
1079
|
+
|
|
1080
|
+
|
|
1081
|
+
class KnowledgeRevisionRecord(StrictModel):
|
|
1082
|
+
id: str
|
|
1083
|
+
item_id: str
|
|
1084
|
+
item_kind: KnowledgeItemKind
|
|
1085
|
+
action: KnowledgeRevisionAction
|
|
1086
|
+
reason: str
|
|
1087
|
+
previous: dict[str, Any] = Field(default_factory=dict)
|
|
1088
|
+
current: dict[str, Any] = Field(default_factory=dict)
|
|
1089
|
+
created_at: datetime
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
class KnowledgeAuditRecord(StrictModel):
|
|
1093
|
+
id: str
|
|
1094
|
+
item_id: str
|
|
1095
|
+
item_kind: KnowledgeItemKind
|
|
1096
|
+
action: KnowledgeAuditAction
|
|
1097
|
+
human_change_version: int = Field(default=0, ge=0)
|
|
1098
|
+
reason: str
|
|
1099
|
+
tool_name: str | None = None
|
|
1100
|
+
task_id: str | None = None
|
|
1101
|
+
session_id: str | None = None
|
|
1102
|
+
use_kind: KnowledgeUseKind | None = None
|
|
1103
|
+
usage_generation: int | None = Field(default=None, ge=1)
|
|
1104
|
+
conflict_check: Literal['no_conflict', 'conflict'] | None = None
|
|
1105
|
+
classification_check: Literal['reasonable', 'needs_change'] | None = None
|
|
1106
|
+
outcome: Literal[
|
|
1107
|
+
'pending_review',
|
|
1108
|
+
'requires_attention',
|
|
1109
|
+
'reviewed',
|
|
1110
|
+
'agent_confirmed',
|
|
1111
|
+
] | None = None
|
|
1112
|
+
created_at: datetime
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
class KnowledgeAgentViewItem(StrictModel):
|
|
1116
|
+
item_id: str = Field(min_length=1, max_length=256)
|
|
1117
|
+
item_kind: KnowledgeItemKind
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
class KnowledgeAgentViewCreate(StrictModel):
|
|
1121
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
1122
|
+
tool_name: str = Field(min_length=1, max_length=128)
|
|
1123
|
+
items: list[KnowledgeAgentViewItem] = Field(max_length=200)
|
|
1124
|
+
|
|
1125
|
+
|
|
1126
|
+
class KnowledgeAgentViewResult(StrictModel):
|
|
1127
|
+
recorded_count: int = Field(ge=0, le=200)
|
|
1128
|
+
item_keys: list[str] = Field(max_length=200)
|
|
1129
|
+
|
|
1130
|
+
|
|
1131
|
+
class KnowledgeAgentReviewCreate(StrictModel):
|
|
1132
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
1133
|
+
item_kind: KnowledgeItemKind
|
|
1134
|
+
human_change_version: int = Field(ge=1)
|
|
1135
|
+
conflict_check: Literal['no_conflict', 'conflict']
|
|
1136
|
+
classification_check: Literal['reasonable', 'needs_change']
|
|
1137
|
+
note: str = Field(min_length=1, max_length=2000)
|
|
1138
|
+
|
|
1139
|
+
|
|
1140
|
+
class KnowledgeHumanChangeSearchRequest(StrictModel):
|
|
1141
|
+
personal_space_id: str = Field(min_length=1, max_length=128)
|
|
1142
|
+
query: str = Field(default='', max_length=2048)
|
|
1143
|
+
status: Literal[
|
|
1144
|
+
'all',
|
|
1145
|
+
'human_changed',
|
|
1146
|
+
'unseen',
|
|
1147
|
+
'viewed',
|
|
1148
|
+
'reviewed',
|
|
1149
|
+
] = 'all'
|
|
1150
|
+
limit: int = Field(default=50, ge=1, le=200)
|
|
1151
|
+
|
|
1152
|
+
|
|
1153
|
+
class KnowledgeHumanChangeItem(StrictModel):
|
|
1154
|
+
item_id: str
|
|
1155
|
+
item_kind: KnowledgeItemKind
|
|
1156
|
+
title: str
|
|
1157
|
+
body: str
|
|
1158
|
+
type: str
|
|
1159
|
+
human_change_status: HumanChangeStatus
|
|
1160
|
+
human_change_version: int = Field(ge=1)
|
|
1161
|
+
last_human_changed_at: datetime
|
|
1162
|
+
last_agent_viewed_at: datetime | None = None
|
|
1163
|
+
last_agent_reviewed_at: datetime | None = None
|
|
1164
|
+
audit_events: list[KnowledgeAuditRecord] = Field(default_factory=list)
|
|
1165
|
+
|
|
1166
|
+
|
|
1167
|
+
class KnowledgeHumanChangeSearchResult(StrictModel):
|
|
1168
|
+
items: list[KnowledgeHumanChangeItem] = Field(default_factory=list)
|
|
1169
|
+
|
|
1170
|
+
|
|
1171
|
+
class KnowledgeAssignmentRecord(StrictModel):
|
|
1172
|
+
id: str
|
|
1173
|
+
item_id: str
|
|
1174
|
+
item_kind: KnowledgeItemKind
|
|
1175
|
+
project_id: str
|
|
1176
|
+
previous_project_id: str | None = None
|
|
1177
|
+
reason: str
|
|
1178
|
+
created_at: datetime
|
|
1179
|
+
updated_at: datetime
|