fuli-context 0.7.4-beta.2 → 0.7.5

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 (133) hide show
  1. package/README.md +93 -7
  2. package/README.zh-CN.md +84 -7
  3. package/compose.graphiti.yml +6 -6
  4. package/dist/web/assets/AboutPage-ByCUor-j.js +1 -0
  5. package/dist/web/assets/ConnectionsPage-DkdkZ40Q.js +1 -0
  6. package/dist/web/assets/KnowledgeEditDialog-DSe3Gwep.js +1 -0
  7. package/dist/web/assets/KnowledgeInspector-CYAjAb9J.js +1 -0
  8. package/dist/web/assets/KnowledgeOrganizerPage-CV4F54R_.js +1 -0
  9. package/dist/web/assets/KnowledgePage-vYYXipOi.js +1 -0
  10. package/dist/web/assets/KnowledgeWorkspace-z4u5mXOG.js +4 -0
  11. package/dist/web/assets/OverviewPage-BWOfeS6k.js +1 -0
  12. package/dist/web/assets/PersonalProfilePage-c3w4-h7a.js +1 -0
  13. package/dist/web/assets/PersonalProjectsPage-B7AlLEwf.js +1 -0
  14. package/dist/web/assets/ProjectAgentsPage-CgftpIPB.css +1 -0
  15. package/dist/web/assets/ProjectAgentsPage-DTHcL_ox.js +11 -0
  16. package/dist/web/assets/PublicProjectsPage-DdH0oGB3.js +1 -0
  17. package/dist/web/assets/ReviewPage-BYIpcIHG.js +1 -0
  18. package/dist/web/assets/SearchableSelect-Dz13ClQb.js +1 -0
  19. package/dist/web/assets/SettingsPage-2VkbXgZi.js +1 -0
  20. package/dist/web/assets/SettingsPage-CwZp2ka4.css +1 -0
  21. package/dist/web/assets/WritingTastePage-BOPQZuva.js +1 -0
  22. package/dist/web/assets/{console-DMvHxKf9.js → console-BlAhujsH.js} +1 -1
  23. package/dist/web/assets/{conversation-launcher-settings-Dnlcw7Rj.js → conversation-launcher-settings-DTYxiLJt.js} +1 -1
  24. package/dist/web/assets/i18n-Ci3heuiL.js +9 -0
  25. package/dist/web/assets/{identity-C9mLdR8L.js → identity-4_-v6cB7.js} +1 -1
  26. package/dist/web/assets/index-BnH5mcZx.css +1 -0
  27. package/dist/web/assets/index-C67Q0JY2.js +2 -0
  28. package/dist/web/assets/{model-CsYAR8O3.js → model-yG0YqUyZ.js} +1 -1
  29. package/dist/web/assets/{useApi-CROJJdhE-BLQiPVw0.js → useApi-CROJJdhE-twhdZArA.js} +1 -1
  30. package/dist/web/assets/vue-router-C-wot_Di.js +1 -0
  31. package/dist/web/index.html +6 -6
  32. package/docs/public-personal-architecture.md +73 -0
  33. package/graph-provider/fuli_graph/app.py +8 -0
  34. package/graph-provider/fuli_graph/collaboration_context.py +69 -6
  35. package/graph-provider/fuli_graph/knowledge_search.py +84 -5
  36. package/graph-provider/fuli_graph/model_validation.py +134 -0
  37. package/graph-provider/fuli_graph/models.py +24 -5
  38. package/graph-provider/fuli_graph/project_agent_access.py +54 -0
  39. package/graph-provider/fuli_graph/project_agent_executor_models.py +635 -0
  40. package/graph-provider/fuli_graph/project_agent_models.py +278 -0
  41. package/graph-provider/fuli_graph/project_agent_routes.py +597 -0
  42. package/graph-provider/fuli_graph/project_agent_task_models.py +542 -0
  43. package/graph-provider/fuli_graph/runtime.py +259 -0
  44. package/graph-provider/fuli_graph/search_projection.py +3 -0
  45. package/graph-provider/fuli_graph/store.py +6 -0
  46. package/graph-provider/fuli_graph/store_knowledge.py +62 -9
  47. package/graph-provider/fuli_graph/store_project_agent_executor_learning.py +756 -0
  48. package/graph-provider/fuli_graph/store_project_agent_executor_routing.py +601 -0
  49. package/graph-provider/fuli_graph/store_project_agent_executors.py +1087 -0
  50. package/graph-provider/fuli_graph/store_project_agent_task_reads.py +458 -0
  51. package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +699 -0
  52. package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +409 -0
  53. package/graph-provider/fuli_graph/store_project_agent_tasks.py +1144 -0
  54. package/graph-provider/fuli_graph/store_project_agents.py +965 -0
  55. package/graph-provider/pyproject.toml +2 -1
  56. package/npm-shrinkwrap.json +2 -2
  57. package/package.json +2 -1
  58. package/skills/capturing-session-knowledge/SKILL.md +43 -2
  59. package/src/adaptive-runtime/executor-pool.js +127 -0
  60. package/src/adaptive-runtime/graph-services.js +166 -0
  61. package/src/adaptive-runtime/lease-client.js +335 -0
  62. package/src/adaptive-runtime/runtime-broker.js +318 -0
  63. package/src/adaptive-runtime/settings.js +111 -0
  64. package/src/adaptive-runtime/state.js +74 -0
  65. package/src/agent-tools/graph-definitions.js +396 -0
  66. package/src/agent-tools/graph-handlers.js +61 -0
  67. package/src/agent-tools/project-agent-definitions.js +307 -0
  68. package/src/backup/graph-backup.js +289 -0
  69. package/src/backup/identity-reconciliation.js +208 -0
  70. package/src/backup/runtime-adapters.js +269 -0
  71. package/src/cli/command-registry.js +7 -2
  72. package/src/cli/graph-data-command.js +112 -0
  73. package/src/cli/local-runtime-command.js +16 -2
  74. package/src/cli/setup-command.js +19 -3
  75. package/src/cli/update-command.js +4 -0
  76. package/src/cli/workspace-connection-command.js +333 -0
  77. package/src/cli.js +11 -0
  78. package/src/graphiti/collaboration-preference-projection.js +29 -1
  79. package/src/graphiti/collaboration-preference-workflow.js +121 -0
  80. package/src/graphiti/federated-application.js +74 -112
  81. package/src/graphiti/knowledge-provider-mapping.js +7 -1
  82. package/src/graphiti/project-agent-control-plane.js +263 -0
  83. package/src/graphiti/project-agent-mapping.js +1003 -0
  84. package/src/graphiti/project-agent-workflows.js +557 -0
  85. package/src/graphiti/provider-client.js +270 -1
  86. package/src/graphiti/runtime-config.js +6 -1
  87. package/src/graphiti/workspace-provider-client.js +403 -0
  88. package/src/http/api-router.js +63 -9
  89. package/src/http/graph-api-router.js +278 -0
  90. package/src/local-runtime/lifecycle.js +46 -10
  91. package/src/mcp/context-resources.js +15 -6
  92. package/src/mcp/create-mcp-server.js +51 -16
  93. package/src/mcp/instructions.js +7 -5
  94. package/src/mcp/runtime.js +17 -5
  95. package/src/mcp/session-id.js +38 -1
  96. package/src/mcp/tool-annotations.js +69 -0
  97. package/src/mcp-server.js +23 -6
  98. package/src/native-runtime/runtime.js +717 -0
  99. package/src/server.js +6 -2
  100. package/src/setup/agent-installation-status.js +7 -1
  101. package/src/setup/agents.js +3 -1
  102. package/src/setup/claude-code-config.js +3 -1
  103. package/src/setup/codex-bootstrap.js +11 -10
  104. package/src/setup/codex-config.js +7 -1
  105. package/src/setup/cursor-config.js +3 -1
  106. package/src/setup/graph-runtime.js +158 -13
  107. package/src/setup/neo4j-memory-profile.js +43 -0
  108. package/src/setup/options.js +38 -2
  109. package/src/setup/paths.js +14 -0
  110. package/src/setup/setup.js +37 -1
  111. package/src/system/resource-monitor.js +181 -16
  112. package/src/system/runtime-settings.js +10 -1
  113. package/src/system/system-service.js +27 -2
  114. package/dist/web/assets/AboutPage-CftRexSY.js +0 -1
  115. package/dist/web/assets/ConnectionsPage-D5J91x6d.js +0 -1
  116. package/dist/web/assets/KnowledgeEditDialog-wotVahvr.js +0 -1
  117. package/dist/web/assets/KnowledgeInspector-BzkW0mVs.js +0 -1
  118. package/dist/web/assets/KnowledgeOrganizerPage-BebUDiHt.js +0 -1
  119. package/dist/web/assets/KnowledgePage-OUT07aKH.js +0 -1
  120. package/dist/web/assets/KnowledgeWorkspace-1pHJ0MNZ.js +0 -4
  121. package/dist/web/assets/OverviewPage-CHE6XhdS.js +0 -1
  122. package/dist/web/assets/PersonalProfilePage-CXXiabUy.js +0 -1
  123. package/dist/web/assets/PersonalProjectsPage-d2wYGW8n.js +0 -1
  124. package/dist/web/assets/PublicProjectsPage-hxLShtnZ.js +0 -1
  125. package/dist/web/assets/ReviewPage-DUeyu7E8.js +0 -1
  126. package/dist/web/assets/SearchableSelect-C5RiB5F8.js +0 -1
  127. package/dist/web/assets/SettingsPage-BMuASSzQ.js +0 -1
  128. package/dist/web/assets/SettingsPage-CAfz4qiV.css +0 -1
  129. package/dist/web/assets/WritingTastePage-B9BCYIwQ.js +0 -1
  130. package/dist/web/assets/i18n-CWfd8Dtt.js +0 -5
  131. package/dist/web/assets/index-X2j29j8i.css +0 -1
  132. package/dist/web/assets/index-nP5p5OaY.js +0 -2
  133. package/dist/web/assets/vue-router-DN8x6iY1.js +0 -1
@@ -0,0 +1,542 @@
1
+ from datetime import date, datetime
2
+ from typing import Literal
3
+
4
+ from pydantic import AliasChoices, Field, field_validator, model_validator
5
+
6
+ from .model_validation import reject_credentials, validate_emoji_sequence
7
+ from .models import SourceApplication, StrictModel
8
+ from .project_agent_models import (
9
+ ProjectAgentExecutorPolicy,
10
+ ProjectAgentModelStrategy,
11
+ ProjectAgentProfile,
12
+ ProjectAgentRecord,
13
+ )
14
+
15
+
16
+ ProjectAgentTaskDuration = Literal['ongoing', 'one_off']
17
+ ProjectAgentStaffingIntent = Literal[
18
+ 'reuse_preferred',
19
+ 'new_durable',
20
+ 'temporary',
21
+ 'unassigned',
22
+ ]
23
+ ProjectAgentTaskStatus = Literal[
24
+ 'awaiting_recruitment',
25
+ 'queued',
26
+ 'running',
27
+ 'paused',
28
+ 'failed',
29
+ 'awaiting_review',
30
+ 'blocked',
31
+ 'completed',
32
+ 'cancelled',
33
+ ]
34
+ ProjectAgentParticipantRole = Literal['lead', 'collaborator']
35
+ ProjectAgentRoutingOutcome = Literal[
36
+ 'assigned_existing',
37
+ 'recruited',
38
+ 'unassigned',
39
+ 'recruitment_required',
40
+ 'blocked',
41
+ ]
42
+ ProjectAgentRoutingReason = Literal[
43
+ 'explicit_agent',
44
+ 'exact_work_kind',
45
+ 'exact_capability',
46
+ 'no_match',
47
+ 'agent_unavailable',
48
+ 'explicit_new_agent',
49
+ 'explicit_temporary_agent',
50
+ 'explicit_unassigned',
51
+ 'hr_unavailable',
52
+ ]
53
+ ProjectAgentPositionKind = Literal['durable', 'temporary']
54
+ ProjectAgentRecruitmentStatus = Literal[
55
+ 'awaiting_confirmation',
56
+ 'requested',
57
+ 'fulfilled',
58
+ 'cancelled',
59
+ 'blocked',
60
+ 'no_hr',
61
+ ]
62
+ ProjectAgentRecruitmentConfirmationMode = Literal[
63
+ 'automatic',
64
+ 'require_confirmation',
65
+ ]
66
+ ProjectAgentTaskComplexity = Literal['simple', 'standard', 'complex']
67
+ ProjectAgentOptimizationPriority = Literal[
68
+ 'quality_and_acceptance',
69
+ 'token_and_cost',
70
+ 'time',
71
+ ]
72
+
73
+
74
+ def _unique_text(value: list[str], label: str) -> list[str]:
75
+ normalized = [item.strip() for item in value]
76
+ if any(not item or len(item) > 512 for item in normalized):
77
+ raise ValueError(f'{label} must contain 1 to 512 characters')
78
+ if len({item.casefold() for item in normalized}) != len(normalized):
79
+ raise ValueError(f'{label} must be unique')
80
+ return normalized
81
+
82
+
83
+ class ProjectAgentParallelPlan(StrictModel):
84
+ enabled: bool = False
85
+ independent_verification: bool = False
86
+ conflict_free_scopes: bool = False
87
+ reason: str | None = Field(default=None, min_length=1, max_length=2048)
88
+ workstream_boundaries: list[str] = Field(default_factory=list, max_length=16)
89
+
90
+ @field_validator('workstream_boundaries')
91
+ @classmethod
92
+ def validate_boundaries(cls, value: list[str]) -> list[str]:
93
+ return _unique_text(value, 'parallel workstream boundaries')
94
+
95
+ @model_validator(mode='after')
96
+ def validate_parallel_plan(self):
97
+ if self.enabled and not (
98
+ self.independent_verification
99
+ and self.conflict_free_scopes
100
+ and self.reason
101
+ and len(self.workstream_boundaries) >= 2
102
+ ):
103
+ raise ValueError(
104
+ 'parallel work requires independent verification, conflict-free '
105
+ 'scopes, a reason, and at least two workstream boundaries'
106
+ )
107
+ if not self.enabled and self.workstream_boundaries:
108
+ raise ValueError('disabled parallel plan cannot contain workstreams')
109
+ return self
110
+
111
+
112
+ class ProjectAgentTaskSubmit(StrictModel):
113
+ personal_space_id: str = Field(min_length=1, max_length=128)
114
+ personal_project_id: str = Field(min_length=1, max_length=128)
115
+ idempotency_key: str = Field(min_length=8, max_length=256)
116
+ title: str = Field(min_length=1, max_length=160)
117
+ objective: str = Field(min_length=1, max_length=4096)
118
+ work_kind: str = Field(min_length=1, max_length=128)
119
+ required_capabilities: list[str] = Field(default_factory=list, max_length=16)
120
+ duration: ProjectAgentTaskDuration = 'ongoing'
121
+ staffing_intent: ProjectAgentStaffingIntent = 'reuse_preferred'
122
+ lead_agent_id: str | None = Field(default=None, min_length=1, max_length=128)
123
+ collaborator_agent_ids: list[str] = Field(default_factory=list, max_length=16)
124
+ coordinator_agent_id: str | None = Field(default=None, min_length=1, max_length=128)
125
+ complexity_hint: ProjectAgentTaskComplexity | None = None
126
+ parallel_plan: ProjectAgentParallelPlan = Field(
127
+ default_factory=ProjectAgentParallelPlan
128
+ )
129
+ model_strategy_override: ProjectAgentModelStrategy | None = None
130
+ executor_policy_override: ProjectAgentExecutorPolicy | None = None
131
+ # Compatibility spelling used by the task store's executor hook.
132
+ executor_override: ProjectAgentExecutorPolicy | None = None
133
+ source_application: SourceApplication | None = None
134
+ source_session_id: str | None = Field(default=None, min_length=1, max_length=256)
135
+ routing_reason: str = Field(min_length=1, max_length=2048)
136
+ recruitment_profile: ProjectAgentProfile | None = None
137
+
138
+ @field_validator('required_capabilities')
139
+ @classmethod
140
+ def validate_capabilities(cls, value: list[str]) -> list[str]:
141
+ return _unique_text(value, 'required capabilities')
142
+
143
+ @field_validator('collaborator_agent_ids')
144
+ @classmethod
145
+ def validate_collaborators(cls, value: list[str]) -> list[str]:
146
+ return _unique_text(value, 'collaborator Agent IDs')
147
+
148
+ @model_validator(mode='after')
149
+ def validate_staffing(self):
150
+ if self.lead_agent_id and self.staffing_intent not in {
151
+ 'reuse_preferred',
152
+ }:
153
+ raise ValueError(
154
+ 'explicit lead Agent requires reuse_preferred staffing intent'
155
+ )
156
+ if self.lead_agent_id in self.collaborator_agent_ids:
157
+ raise ValueError('lead Agent cannot also be a collaborator')
158
+ # The coordinator may fill missing collaborators from qualified durable
159
+ # assignments; the final participant minimum is checked after routing.
160
+ if self.staffing_intent == 'temporary' and self.duration != 'one_off':
161
+ raise ValueError('temporary staffing requires a one-off task')
162
+ if (
163
+ self.recruitment_profile
164
+ and self.recruitment_profile.agent_type != 'durable'
165
+ ):
166
+ raise ValueError('recruitment profile agent_type must be durable input')
167
+ if self.executor_policy_override and self.executor_override:
168
+ raise ValueError(
169
+ 'executor_policy_override and executor_override are mutually exclusive'
170
+ )
171
+ reject_credentials(self, 'project Agent task')
172
+ return self
173
+
174
+
175
+ class ProjectAgentTaskParticipantRecord(StrictModel):
176
+ agent_id: str
177
+ role: ProjectAgentParticipantRole
178
+ status: ProjectAgentTaskStatus
179
+ assignment_summary: str | None = None
180
+ joined_at: datetime
181
+ updated_at: datetime
182
+ ended_at: datetime | None = None
183
+
184
+
185
+ class ProjectAgentTaskExecutionSummary(StrictModel):
186
+ """Auditable execution evidence projected onto a real task participant.
187
+
188
+ The participant identity is durable Agent data; the optional worker fields
189
+ identify a concrete subagent run when a client reports one. None means the
190
+ provider did not observe that fact and must not infer it from routing
191
+ configuration.
192
+ """
193
+
194
+ agent_id: str
195
+ agent_name: str | None = None
196
+ occupation_emoji: str | None = None
197
+ participant_role: ProjectAgentParticipantRole
198
+ executor: str | None = None
199
+ executor_id: str | None = None
200
+ source_application: SourceApplication | None = None
201
+ actual_model_provider: str | None = None
202
+ actual_model: str | None = None
203
+ work_summary: str | None = None
204
+ status: ProjectAgentTaskStatus
205
+ worker_id: str | None = None
206
+ worker_label: str | None = None
207
+ worker_occupation_emoji: str | None = None
208
+
209
+ @field_validator('occupation_emoji', 'worker_occupation_emoji', mode='before')
210
+ @classmethod
211
+ def validate_emojis(cls, value):
212
+ return validate_emoji_sequence(value, 'occupation emoji')
213
+
214
+
215
+ class ProjectAgentTaskEventRecord(StrictModel):
216
+ event_id: str
217
+ task_id: str
218
+ agent_id: str | None = None
219
+ status: ProjectAgentTaskStatus
220
+ actor_kind: Literal['agent', 'human', 'system', 'hr']
221
+ summary: str
222
+ source_application: SourceApplication | None = None
223
+ source_session_id: str | None = None
224
+ actual_model_provider: str | None = None
225
+ actual_model: str | None = None
226
+ actual_executor_id: str | None = None
227
+ executor_rule_id: str | None = None
228
+ matched_executor_rule_id: str | None = None
229
+ executor_selection_reason: str | None = None
230
+ executor_fallback_reason: str | None = None
231
+ executor_blocked_reason: str | None = None
232
+ worker_id: str | None = Field(
233
+ default=None,
234
+ validation_alias=AliasChoices('worker_id', 'workerId'),
235
+ )
236
+ worker_label: str | None = Field(
237
+ default=None,
238
+ validation_alias=AliasChoices('worker_label', 'workerLabel'),
239
+ )
240
+ worker_occupation_emoji: str | None = Field(
241
+ default=None,
242
+ validation_alias=AliasChoices(
243
+ 'worker_occupation_emoji',
244
+ 'workerOccupationEmoji',
245
+ ),
246
+ )
247
+ worker_status: ProjectAgentTaskStatus | None = Field(
248
+ default=None,
249
+ validation_alias=AliasChoices('worker_status', 'workerStatus'),
250
+ )
251
+ created_at: datetime
252
+
253
+ @field_validator('worker_occupation_emoji', mode='before')
254
+ @classmethod
255
+ def validate_worker_occupation_emoji(cls, value):
256
+ return validate_emoji_sequence(value, 'worker occupation emoji')
257
+
258
+
259
+ class ProjectAgentRoutingDecisionRecord(StrictModel):
260
+ decision_id: str
261
+ task_id: str
262
+ coordinator_agent_id: str
263
+ complexity: ProjectAgentTaskComplexity
264
+ complexity_basis: list[str] = Field(default_factory=list)
265
+ selected_model_strategy: ProjectAgentModelStrategy | None = None
266
+ model_strategy_source: Literal['agent', 'assignment', 'task', 'coordinator']
267
+ outcome: ProjectAgentRoutingOutcome
268
+ reason: ProjectAgentRoutingReason
269
+ match_basis: list[str] = Field(default_factory=list)
270
+ candidate_agent_ids: list[str] = Field(default_factory=list)
271
+ optimization_priority: list[ProjectAgentOptimizationPriority] = Field(
272
+ default_factory=lambda: [
273
+ 'quality_and_acceptance',
274
+ 'token_and_cost',
275
+ 'time',
276
+ ]
277
+ )
278
+ parallel_plan: ProjectAgentParallelPlan = Field(
279
+ default_factory=ProjectAgentParallelPlan
280
+ )
281
+ selected_executor_id: str | None = None
282
+ executor_rule_id: str | None = None
283
+ matched_executor_rule_id: str | None = None
284
+ executor_selection_reason: str | None = None
285
+ executor_fallback_outcome: str | None = None
286
+ executor_blocked_reason: str | None = None
287
+ executor_decision: dict[str, object] | None = None
288
+ executor_policy: ProjectAgentExecutorPolicy = Field(
289
+ default_factory=ProjectAgentExecutorPolicy
290
+ )
291
+ created_at: datetime
292
+
293
+
294
+ class ProjectAgentTaskRecord(StrictModel):
295
+ task_id: str
296
+ personal_space_id: str
297
+ personal_project_id: str
298
+ title: str
299
+ objective: str
300
+ work_kind: str
301
+ required_capabilities: list[str]
302
+ duration: ProjectAgentTaskDuration
303
+ staffing_intent: ProjectAgentStaffingIntent
304
+ status: ProjectAgentTaskStatus
305
+ revision: int = Field(ge=0)
306
+ routing_outcome: ProjectAgentRoutingOutcome
307
+ routing_reason: ProjectAgentRoutingReason
308
+ routing_explanation: str
309
+ match_basis: list[str] = Field(default_factory=list)
310
+ coordinator_agent_id: str
311
+ complexity: ProjectAgentTaskComplexity
312
+ complexity_basis: list[str] = Field(default_factory=list)
313
+ routing_decision: ProjectAgentRoutingDecisionRecord
314
+ lead_agent_id: str | None = None
315
+ participants: list[ProjectAgentTaskParticipantRecord] = Field(default_factory=list)
316
+ execution_summary: list[ProjectAgentTaskExecutionSummary] = Field(
317
+ default_factory=list,
318
+ validation_alias=AliasChoices('execution_summary', 'executionSummary'),
319
+ )
320
+ effective_model_strategy: ProjectAgentModelStrategy | None = None
321
+ model_strategy_source: Literal[
322
+ 'agent',
323
+ 'assignment',
324
+ 'task',
325
+ 'coordinator',
326
+ ] = 'coordinator'
327
+ executor_policy: ProjectAgentExecutorPolicy = Field(
328
+ default_factory=ProjectAgentExecutorPolicy
329
+ )
330
+ selected_executor_id: str | None = None
331
+ executor_rule_id: str | None = None
332
+ actual_run_id: str | None = None
333
+ actual_executor_id: str | None = None
334
+ actual_model_provider: str | None = None
335
+ actual_model: str | None = None
336
+ matched_executor_rule_id: str | None = None
337
+ executor_selection_reason: str | None = None
338
+ executor_fallback_outcome: str | None = None
339
+ executor_fallback_reason: str | None = None
340
+ executor_blocked_reason: str | None = None
341
+ executor_decision: dict[str, object] | None = None
342
+ hr_agent_id: str | None = None
343
+ recruitment_id: str | None = None
344
+ source_application: SourceApplication | None = None
345
+ source_session_id: str | None = None
346
+ result_summary: str | None = None
347
+ failure_reason: str | None = None
348
+ created_at: datetime
349
+ updated_at: datetime
350
+ completed_at: datetime | None = None
351
+ events: list[ProjectAgentTaskEventRecord] = Field(default_factory=list)
352
+
353
+
354
+ class ProjectAgentTaskActivityCreate(StrictModel):
355
+ personal_space_id: str = Field(min_length=1, max_length=128)
356
+ personal_project_id: str = Field(min_length=1, max_length=128)
357
+ task_id: str = Field(min_length=1, max_length=128)
358
+ idempotency_key: str = Field(min_length=8, max_length=256)
359
+ expected_revision: int | None = Field(default=None, ge=0)
360
+ status: ProjectAgentTaskStatus
361
+ summary: str = Field(min_length=1, max_length=4096)
362
+ agent_id: str | None = Field(default=None, min_length=1, max_length=128)
363
+ actor_kind: Literal['agent', 'human'] = 'agent'
364
+ source_application: SourceApplication | None = None
365
+ source_session_id: str | None = Field(default=None, min_length=1, max_length=256)
366
+ actual_model_provider: str | None = Field(default=None, min_length=1, max_length=128)
367
+ actual_model: str | None = Field(default=None, min_length=1, max_length=256)
368
+ actual_executor_id: str | None = Field(default=None, min_length=1, max_length=128)
369
+ matched_executor_rule_id: str | None = Field(
370
+ default=None,
371
+ min_length=1,
372
+ max_length=128,
373
+ )
374
+ executor_selection_reason: str | None = Field(
375
+ default=None,
376
+ min_length=1,
377
+ max_length=2048,
378
+ )
379
+ executor_fallback_reason: str | None = Field(
380
+ default=None,
381
+ min_length=1,
382
+ max_length=2048,
383
+ )
384
+ executor_blocked_reason: str | None = Field(
385
+ default=None,
386
+ min_length=1,
387
+ max_length=2048,
388
+ )
389
+ worker_id: str | None = Field(
390
+ default=None,
391
+ min_length=1,
392
+ max_length=128,
393
+ validation_alias=AliasChoices('worker_id', 'workerId'),
394
+ )
395
+ worker_label: str | None = Field(
396
+ default=None,
397
+ min_length=1,
398
+ max_length=160,
399
+ validation_alias=AliasChoices('worker_label', 'workerLabel'),
400
+ )
401
+ worker_occupation_emoji: str | None = Field(
402
+ default=None,
403
+ min_length=1,
404
+ max_length=32,
405
+ validation_alias=AliasChoices(
406
+ 'worker_occupation_emoji',
407
+ 'workerOccupationEmoji',
408
+ ),
409
+ )
410
+ worker_status: ProjectAgentTaskStatus | None = Field(
411
+ default=None,
412
+ validation_alias=AliasChoices('worker_status', 'workerStatus'),
413
+ )
414
+
415
+ @field_validator('worker_occupation_emoji', mode='before')
416
+ @classmethod
417
+ def validate_worker_occupation_emoji(cls, value):
418
+ return validate_emoji_sequence(value, 'worker occupation emoji')
419
+
420
+ @model_validator(mode='after')
421
+ def validate_actual_model(self):
422
+ if self.status in {'completed', 'failed'} and not self.agent_id:
423
+ raise ValueError(
424
+ 'completed or failed activity requires the participating Agent ID'
425
+ )
426
+ if bool(self.actual_model_provider) != bool(self.actual_model):
427
+ raise ValueError('actual model provider and model must be reported together')
428
+ if bool(self.actual_executor_id) != bool(self.actual_model):
429
+ raise ValueError(
430
+ 'actual executor, model provider, and model must be reported together'
431
+ )
432
+ if self.actual_executor_id and not self.agent_id:
433
+ raise ValueError('actual execution report requires the participating Agent ID')
434
+ if self.worker_id and not self.agent_id:
435
+ raise ValueError('worker execution report requires the participating Agent ID')
436
+ if (self.worker_label or self.worker_occupation_emoji) and not self.worker_id:
437
+ raise ValueError(
438
+ 'worker label or occupation emoji requires a worker ID'
439
+ )
440
+ reject_credentials(self, 'project Agent task activity')
441
+ return self
442
+
443
+
444
+ class ProjectAgentRecruitmentRecord(StrictModel):
445
+ recruitment_id: str
446
+ personal_space_id: str
447
+ personal_project_id: str
448
+ task_id: str
449
+ coordinator_agent_id: str
450
+ hr_agent_id: str | None = None
451
+ position_kind: ProjectAgentPositionKind
452
+ work_kind: str
453
+ required_capabilities: list[str]
454
+ reason_code: ProjectAgentRoutingReason
455
+ reason: str
456
+ status: ProjectAgentRecruitmentStatus
457
+ confirmation_mode: ProjectAgentRecruitmentConfirmationMode
458
+ proposed_agent_id: str
459
+ proposed_profile: ProjectAgentProfile
460
+ trigger_source_application: SourceApplication | None = None
461
+ trigger_source_session_id: str | None = None
462
+ revision: int = Field(ge=0)
463
+ recruited_agent_id: str | None = None
464
+ test_source: str | None = Field(default=None, min_length=1, max_length=256)
465
+ cleanup_eligible: bool = False
466
+ created_at: datetime
467
+ updated_at: datetime
468
+ fulfilled_at: datetime | None = None
469
+
470
+
471
+ class ProjectAgentTaskRouteResult(StrictModel):
472
+ task: ProjectAgentTaskRecord
473
+ assigned_agent: ProjectAgentRecord | None = None
474
+ recruitment: ProjectAgentRecruitmentRecord | None = None
475
+ decision: Literal[
476
+ 'reused',
477
+ 'recruited',
478
+ 'unassigned',
479
+ 'awaiting_confirmation',
480
+ 'blocked',
481
+ ]
482
+ must_disclose_recruitment: bool = False
483
+ client_notice: str | None = None
484
+
485
+
486
+ class ProjectAgentRecruitmentPolicyRecord(StrictModel):
487
+ personal_space_id: str
488
+ confirmation_mode: ProjectAgentRecruitmentConfirmationMode = 'automatic'
489
+ updated_at: datetime | None = None
490
+
491
+
492
+ class ProjectAgentRecruitmentPolicyUpdate(StrictModel):
493
+ personal_space_id: str = Field(min_length=1, max_length=128)
494
+ confirmation_mode: ProjectAgentRecruitmentConfirmationMode
495
+
496
+
497
+ class ProjectAgentRecruitmentDecision(StrictModel):
498
+ personal_space_id: str = Field(min_length=1, max_length=128)
499
+ personal_project_id: str = Field(min_length=1, max_length=128)
500
+ recruitment_id: str = Field(min_length=1, max_length=128)
501
+ expected_revision: int = Field(ge=0)
502
+ decision: Literal['approve', 'cancel']
503
+ reason: str = Field(min_length=1, max_length=4096)
504
+
505
+ @model_validator(mode='after')
506
+ def reject_sensitive_values(self):
507
+ reject_credentials(self, 'project Agent recruitment decision')
508
+ return self
509
+
510
+
511
+ class ProjectAgentActivityTask(StrictModel):
512
+ task_id: str
513
+ title: str
514
+ status: Literal['completed', 'failed', 'cancelled']
515
+ summary: str
516
+ occurred_at: datetime
517
+ source_application: SourceApplication | None = None
518
+ actual_model_provider: str | None = None
519
+ actual_model: str | None = None
520
+ actual_executor_id: str | None = None
521
+ matched_executor_rule_id: str | None = None
522
+ worker_id: str | None = None
523
+ worker_label: str | None = None
524
+ worker_occupation_emoji: str | None = None
525
+ worker_status: ProjectAgentTaskStatus | None = None
526
+
527
+
528
+ class ProjectAgentActivityDay(StrictModel):
529
+ date: date
530
+ completed: int = Field(default=0, ge=0)
531
+ failed: int = Field(default=0, ge=0)
532
+ cancelled: int = Field(default=0, ge=0)
533
+ total: int = Field(default=0, ge=0)
534
+ tasks: list[ProjectAgentActivityTask] = Field(default_factory=list)
535
+
536
+
537
+ class ProjectAgentActivityResult(StrictModel):
538
+ agent_id: str
539
+ personal_space_id: str
540
+ from_date: date
541
+ to_date: date
542
+ days: list[ProjectAgentActivityDay] = Field(default_factory=list)