fuli-context 0.9.0 → 0.11.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.
Files changed (210) hide show
  1. package/README.md +29 -58
  2. package/README.zh-CN.md +24 -54
  3. package/dist/web/assets/AboutPage-CKq4IRHd.js +1 -0
  4. package/dist/web/assets/AgentConversations-DfXiFvBN.js +2 -0
  5. package/dist/web/assets/AgentConversations-XqNEBY_2.css +1 -0
  6. package/dist/web/assets/AgentHand-BBM-sKh2.js +1 -0
  7. package/dist/web/assets/AgentName-C_6R80nP.js +1 -0
  8. package/dist/web/assets/AgentProfilePage-Cz-i1fRD.js +1 -0
  9. package/dist/web/assets/AgentsDirectoryPage-BI0EFz6y.js +1 -0
  10. package/dist/web/assets/AgentsDirectoryPage-yRWktiin.css +1 -0
  11. package/dist/web/assets/ConnectionsPage-BoC6kXgs.js +1 -0
  12. package/dist/web/assets/ConnectionsPage-CM9C-zEI.css +1 -0
  13. package/dist/web/assets/EmployeeRecruitDialog-BVyclQAJ.js +1 -0
  14. package/dist/web/assets/EmployeeRecruitDialog-CVbqCzHb.css +1 -0
  15. package/dist/web/assets/EmployeeWorkbenchPage-D-c_Jm2z.css +1 -0
  16. package/dist/web/assets/EmployeeWorkbenchPage-D2GvYraI.js +3 -0
  17. package/dist/web/assets/KnowledgeEditDialog-a0AKWics.js +1 -0
  18. package/dist/web/assets/KnowledgeEditDialog-hT9znkoe.css +1 -0
  19. package/dist/web/assets/KnowledgeInspector-DMg9yJZ2.js +1 -0
  20. package/dist/web/assets/KnowledgeOrganizerPage-BgsHM3Qw.js +1 -0
  21. package/dist/web/assets/KnowledgeOrganizerPage-DeL7Jp34.css +1 -0
  22. package/dist/web/assets/KnowledgePage-kn8MqUCO.js +1 -0
  23. package/dist/web/assets/KnowledgeWorkspace-BtRq9fvC.js +4 -0
  24. package/dist/web/assets/KnowledgeWorkspace-D4a7FcAb.css +1 -0
  25. package/dist/web/assets/NotFoundPage-D-7jLz3o.js +1 -0
  26. package/dist/web/assets/OverviewPage-BHPc-XxY.js +1 -0
  27. package/dist/web/assets/OverviewPage-By-fUzSG.css +1 -0
  28. package/dist/web/assets/PersonalProfilePage-C00iV0Kx.js +1 -0
  29. package/dist/web/assets/PersonalProfilePage-DrMDAAdy.css +1 -0
  30. package/dist/web/assets/PersonalProjectsPage-CFNkdHSo.js +1 -0
  31. package/dist/web/assets/ProjectAgentFirstTask-Bi1ZsTC1.css +1 -0
  32. package/dist/web/assets/ProjectAgentFirstTask-CrCWEYgn.js +1 -0
  33. package/dist/web/assets/ProjectAgentsPage-BzztLMH-.js +12 -0
  34. package/dist/web/assets/ProjectAgentsPage-DNLNjYp-.css +1 -0
  35. package/dist/web/assets/ProjectScopePicker-CMxn3txv.js +1 -0
  36. package/dist/web/assets/PublicProjectsPage-DKamXDV9.css +1 -0
  37. package/dist/web/assets/PublicProjectsPage-DQVtvhOS.js +1 -0
  38. package/dist/web/assets/ReviewPage-BZhvyDWc.js +1 -0
  39. package/dist/web/assets/RoundtablesPage-CVebTXQa.js +1 -0
  40. package/dist/web/assets/RoundtablesPage-Cb0R4GaM.css +1 -0
  41. package/dist/web/assets/SettingsPage-1knvfOW6.js +1 -0
  42. package/dist/web/assets/SettingsPage-BmYkLul4.css +1 -0
  43. package/dist/web/assets/SharedJefaReportPage-BCVq8u0P.js +1 -0
  44. package/dist/web/assets/UiDialog-Da1P10f4.js +1 -0
  45. package/dist/web/assets/UiDisclosure-8wE8YuE4.js +1 -0
  46. package/dist/web/assets/UiSegmented-BKZXc8QF.js +1 -0
  47. package/dist/web/assets/UiSelect-Bv6R0yr1.css +1 -0
  48. package/dist/web/assets/UiSelect-D5Xj5Ec4.js +1 -0
  49. package/dist/web/assets/WritingTastePage-B8-etOBn.js +1 -0
  50. package/dist/web/assets/WritingTastePage-B_zYD1df.css +1 -0
  51. package/dist/web/assets/agent-profile-BZj3ezWe.css +1 -0
  52. package/dist/web/assets/{attention-store-Dk-_P4Oi.js → attention-store-BfxtL9EV.js} +1 -1
  53. package/dist/web/assets/{catalog-DN72452f.js → catalog-6SN9i3WR.js} +1 -1
  54. package/dist/web/assets/client-CUTkF3t4.js +1 -0
  55. package/dist/web/assets/{console-BtPqo79j.js → console-CG8tdtSs.js} +1 -1
  56. package/dist/web/assets/{conversation-launcher-settings-jkHRmWai.js → conversation-launcher-settings-CyZkLJg7.js} +1 -1
  57. package/dist/web/assets/{i18n-D2CJkEYK.js → i18n-CCMR4gDS.js} +3 -3
  58. package/dist/web/assets/{identity-B3zEw-1u.js → identity-C1eN-FSB.js} +1 -1
  59. package/dist/web/assets/index-kPB0Cf8Q.css +1 -0
  60. package/dist/web/assets/index-sLDKI6R-.js +5 -0
  61. package/dist/web/assets/{model-S7ehAjky.js → model-l-sAqtW_.js} +1 -1
  62. package/dist/web/assets/{personal-profile-graph--GG3Mizo.js → personal-profile-graph-BD1Y6TDS.js} +1 -1
  63. package/dist/web/assets/shared-report-B46dxlCR.js +1 -0
  64. package/dist/web/assets/{useAgentRoster-BAPKWZ9y.js → useAgentRoster-DdxoKDEE.js} +1 -1
  65. package/dist/web/assets/{useApi-CROJJdhE-BtFMXFjw.js → useApi-CROJJdhE-DlOiu8lP.js} +1 -1
  66. package/dist/web/assets/{vue-router-DkaTtPn0.js → vue-router-DjqEU4W2.js} +1 -1
  67. package/dist/web/index.html +14 -16
  68. package/docs/agent-conversations-and-collaboration.md +9 -9
  69. package/docs/agent-roundtable.md +61 -0
  70. package/docs/employee-agents.md +2 -2
  71. package/docs/project-agent-memory.md +1 -1
  72. package/graph-provider/fuli_graph/agent_conversation_models.py +11 -2
  73. package/graph-provider/fuli_graph/conversation_context.py +40 -9
  74. package/graph-provider/fuli_graph/default_project_lead.py +88 -0
  75. package/graph-provider/fuli_graph/project_agent_memory_routes.py +7 -0
  76. package/graph-provider/fuli_graph/project_agent_task_models.py +3 -0
  77. package/graph-provider/fuli_graph/runtime.py +3 -76
  78. package/graph-provider/fuli_graph/store.py +2 -0
  79. package/graph-provider/fuli_graph/store_agent_conversations.py +60 -8
  80. package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +3 -1
  81. package/graph-provider/fuli_graph/store_project_agent_tasks.py +12 -8
  82. package/graph-provider/fuli_graph/store_project_agents.py +25 -124
  83. package/graph-provider/fuli_graph/store_task_contexts.py +26 -0
  84. package/graph-provider/fuli_graph/system_agents.py +128 -0
  85. package/graph-provider/fuli_graph/system_hr_identity.py +1 -1
  86. package/graph-provider/fuli_graph/task_context_routes.py +10 -0
  87. package/graph-provider/pyproject.toml +1 -1
  88. package/npm-shrinkwrap.json +2 -2
  89. package/package.json +2 -4
  90. package/skills/capturing-session-knowledge/SKILL.md +1 -1
  91. package/src/agent-roundtable/http.js +14 -0
  92. package/src/agent-roundtable/service.js +218 -0
  93. package/src/agent-roundtable/store.js +86 -0
  94. package/src/agent-roundtable/tools.js +61 -0
  95. package/src/agent-roundtable/wake.js +99 -0
  96. package/src/agent-tools/agent-conversation-definitions.js +3 -3
  97. package/src/agent-tools/graph-definitions.js +3 -2
  98. package/src/agent-tools/interface-catalog.js +0 -5
  99. package/src/agent-tools.js +2 -2
  100. package/src/agents/claude-code/lifecycle-hook.js +6 -19
  101. package/src/agents/claude-code/wake.js +37 -0
  102. package/src/agents/codex/lifecycle-hook.js +51 -27
  103. package/src/agents/codex/token-usage.js +235 -0
  104. package/src/agents/codex/wake.js +47 -0
  105. package/src/agents/cursor/lifecycle-hook.js +3 -9
  106. package/src/agents/identity-receipt.js +16 -6
  107. package/src/agents/session-log.js +34 -0
  108. package/src/agents/token-usage-resolver.js +147 -0
  109. package/src/agents/wake-registry.js +14 -0
  110. package/src/cli/command-registry.js +0 -2
  111. package/src/cli/setup-command.js +2 -5
  112. package/src/cli/update-command.js +13 -2
  113. package/src/cli.js +0 -5
  114. package/src/employees/management-policy.js +4 -2
  115. package/src/employees/manifest.js +3 -0
  116. package/src/employees/recruitment.js +3 -2
  117. package/src/graphiti/agent-conversations.js +11 -9
  118. package/src/graphiti/agent-knowledge-workflows.js +34 -7
  119. package/src/graphiti/federated-application.js +18 -6
  120. package/src/graphiti/project-agent-mapping.js +1 -1
  121. package/src/graphiti/project-agent-task-entry.js +19 -7
  122. package/src/graphiti/project-agent-workflows.js +6 -1
  123. package/src/graphiti/project-path-context.js +7 -2
  124. package/src/graphiti/provider-client.js +8 -0
  125. package/src/graphiti/repository-project-registration.js +17 -0
  126. package/src/graphiti/task-entry-preferences.js +5 -0
  127. package/src/http/api-router.js +5 -3
  128. package/src/http/error-mapping.js +0 -2
  129. package/src/mcp/instructions.js +5 -5
  130. package/src/mcp/session-id.js +1 -1
  131. package/src/mcp/tool-annotations.js +3 -2
  132. package/src/setup/agents.js +1 -41
  133. package/src/setup/graph-runtime.js +15 -2
  134. package/src/setup/session-skill.js +22 -2
  135. package/dist/web/assets/AboutPage-D-GyJNv1.js +0 -1
  136. package/dist/web/assets/AgentConversations-DOuZgUeA.js +0 -2
  137. package/dist/web/assets/AgentConversations-DS8h3yKn.css +0 -1
  138. package/dist/web/assets/AgentHand-eGsXoGSq.js +0 -1
  139. package/dist/web/assets/AgentName-Dtmu9Ff5.js +0 -1
  140. package/dist/web/assets/AgentProfilePage-D8Tvayn7.js +0 -1
  141. package/dist/web/assets/AgentsDirectoryPage-BTEfKZjF.js +0 -1
  142. package/dist/web/assets/ConnectionsPage-C1w99yN3.css +0 -1
  143. package/dist/web/assets/ConnectionsPage-DwOIcvl9.js +0 -1
  144. package/dist/web/assets/EmployeeRecruitDialog-BrIMRblq.css +0 -1
  145. package/dist/web/assets/EmployeeRecruitDialog-D11blfwU.js +0 -1
  146. package/dist/web/assets/EmployeeWorkbenchPage-BcEzYt65.css +0 -1
  147. package/dist/web/assets/EmployeeWorkbenchPage-CK2BAoGt.js +0 -3
  148. package/dist/web/assets/GrowthLoading-KTjE2e50.js +0 -1
  149. package/dist/web/assets/KnowledgeEditDialog-B3x8vs3c.css +0 -1
  150. package/dist/web/assets/KnowledgeEditDialog-n9trHQ5d.js +0 -1
  151. package/dist/web/assets/KnowledgeInspector-BcNjgw9H.js +0 -1
  152. package/dist/web/assets/KnowledgeOrganizerPage-BzgxP-nG.css +0 -1
  153. package/dist/web/assets/KnowledgeOrganizerPage-DMBpGxLr.js +0 -1
  154. package/dist/web/assets/KnowledgePage-POkCMW8f.js +0 -1
  155. package/dist/web/assets/KnowledgeWorkspace-2FvQadhB.js +0 -4
  156. package/dist/web/assets/KnowledgeWorkspace-sb8OJ97I.css +0 -1
  157. package/dist/web/assets/NotFoundPage-BCMZbeB8.js +0 -1
  158. package/dist/web/assets/OverviewPage-0CFWCG9-.css +0 -1
  159. package/dist/web/assets/OverviewPage-0mYifpjp.js +0 -1
  160. package/dist/web/assets/PersonalProfilePage-Chv2a2pi.css +0 -1
  161. package/dist/web/assets/PersonalProfilePage-JW6Muix6.js +0 -1
  162. package/dist/web/assets/PersonalProjectsPage-CRbi64JP.js +0 -1
  163. package/dist/web/assets/ProjectAgentFirstTask-6afJ8HUe.css +0 -1
  164. package/dist/web/assets/ProjectAgentFirstTask-DJ0wTbh8.js +0 -1
  165. package/dist/web/assets/ProjectAgentsPage-BgQLIpxg.css +0 -1
  166. package/dist/web/assets/ProjectAgentsPage-CQemHFxy.js +0 -12
  167. package/dist/web/assets/ProjectScopePicker-BwB7gK-7.js +0 -1
  168. package/dist/web/assets/PublicProjectsPage-CB272Sq-.js +0 -1
  169. package/dist/web/assets/ReviewPage-CelTZU4C.js +0 -1
  170. package/dist/web/assets/RoundtablesPage-ByVeZlgC.js +0 -2
  171. package/dist/web/assets/RoundtablesPage-DCbIBnBq.css +0 -1
  172. package/dist/web/assets/SearchableSelect-f7EHbyDL.js +0 -1
  173. package/dist/web/assets/SettingsPage-5g7mRN2F.css +0 -1
  174. package/dist/web/assets/SettingsPage-nAijmm5f.js +0 -1
  175. package/dist/web/assets/SharedJefaReportPage-CjpJ9bkk.js +0 -1
  176. package/dist/web/assets/UiDisclosure-CWlC91Zt.js +0 -1
  177. package/dist/web/assets/WritingTastePage-DaHWY61P.js +0 -1
  178. package/dist/web/assets/WritingTastePage-SMGF39mf.css +0 -1
  179. package/dist/web/assets/agent-profile-CBvJHGjt.css +0 -1
  180. package/dist/web/assets/client-B2cRl-7c.js +0 -1
  181. package/dist/web/assets/copy-D2LqwtPZ.js +0 -1
  182. package/dist/web/assets/index-CNp0tRNp.css +0 -1
  183. package/dist/web/assets/index-UygXwZDZ.js +0 -5
  184. package/dist/web/assets/shared-report-DEK9wvhl.js +0 -1
  185. package/dist/web/assets/useModalDialog-D8tiJfbc.js +0 -1
  186. package/docs/roundtable-beta-testing.md +0 -102
  187. package/docs/roundtable-beta-validation.md +0 -133
  188. package/docs/roundtable-research.md +0 -142
  189. package/src/agents/a2a/roundtable-participant.js +0 -374
  190. package/src/agents/claude-code/roundtable-participant.js +0 -24
  191. package/src/agents/codex/roundtable-config.js +0 -32
  192. package/src/agents/codex/roundtable-participant.js +0 -34
  193. package/src/agents/grok/roundtable-participant.js +0 -185
  194. package/src/agents/pi/roundtable-participant.js +0 -193
  195. package/src/agents/pi/roundtable-result-format.js +0 -183
  196. package/src/cli/roundtable-command.js +0 -79
  197. package/src/roundtables/application.js +0 -69
  198. package/src/roundtables/domain.js +0 -181
  199. package/src/roundtables/http-router.js +0 -56
  200. package/src/roundtables/mcp-server.js +0 -48
  201. package/src/roundtables/participant-registry.js +0 -12
  202. package/src/roundtables/process.js +0 -70
  203. package/src/roundtables/result-contract.js +0 -47
  204. package/src/roundtables/server.js +0 -52
  205. package/src/roundtables/service.js +0 -396
  206. package/src/roundtables/store.js +0 -59
  207. package/src/roundtables/tool-contract.js +0 -41
  208. package/src/roundtables/turn-prompt.js +0 -24
  209. package/src/roundtables/worker.js +0 -85
  210. /package/dist/web/assets/{GrowthLoading-CIuQ1icy.css → client-CIuQ1icy.css} +0 -0
@@ -347,13 +347,16 @@ class StoreProjectAgentTasks(
347
347
  reason = 'hr_unavailable'
348
348
  self._verify_parallel_plan(request.parallel_plan, participants)
349
349
 
350
- executor_decision = await self._resolve_executor_if_available(
351
- actor,
352
- request,
353
- lead,
354
- model_strategy,
355
- model_source,
356
- task_id,
350
+ # Staffing a default lead executes nothing, so it needs no executor.
351
+ executor_decision = None if request.staffing_intent == 'default_lead' else (
352
+ await self._resolve_executor_if_available(
353
+ actor,
354
+ request,
355
+ lead,
356
+ model_strategy,
357
+ model_source,
358
+ task_id,
359
+ )
357
360
  )
358
361
  if lead and executor_decision and self._executor_decision_blocked(executor_decision):
359
362
  routing_outcome = 'blocked'
@@ -904,7 +907,8 @@ class StoreProjectAgentTasks(
904
907
  lead_agent_id=lead_agent_id,
905
908
  complexity=complexity,
906
909
  complexity_basis=complexity_basis,
907
- verification_required=request.verification_required,
910
+ # A staffing record has no artifact to verify.
911
+ verification_required=request.staffing_intent != 'default_lead',
908
912
  model_strategy_json=model_strategy.model_dump_json() if model_strategy else None,
909
913
  task_model_strategy_override_json=(
910
914
  request.model_strategy_override.model_dump_json()
@@ -20,146 +20,42 @@ from .project_agent_models import (
20
20
  from .provider_values import native_datetime, now_utc, stable_uuid
21
21
  from .project_agent_names import agent_display_name
22
22
  from .store_transactions import query_store_transaction
23
+ from .system_agents import (
24
+ SYSTEM_COORDINATOR_AGENT_ID,
25
+ SYSTEM_HR_AGENT_ID,
26
+ ensure_system_agents,
27
+ )
23
28
  from .system_hr_identity import LEGACY_HR_AGENT_ID, merge_legacy_hr, resolve_hr_alias
24
29
 
25
30
 
26
- SYSTEM_COORDINATOR_AGENT_ID = 'fuli-project-coordinator'
27
- SYSTEM_HR_AGENT_ID = 'employee.bole'
28
-
29
-
30
31
  class StoreProjectAgents:
31
32
  async def ensure_system_project_hr(
32
33
  self,
33
34
  actor: dict,
34
35
  personal_space_id: str,
35
36
  ) -> ProjectAgentRecord:
36
- self._require_personal()
37
- await self.authorize(actor, personal_space_id, 'maintainer')
38
- async with query_store_transaction(self) as scoped:
39
- await scoped._ensure_system_project_hr_identity(personal_space_id)
40
- await merge_legacy_hr(scoped, personal_space_id)
37
+ await self._ensure_system_agents(actor, personal_space_id)
41
38
  return await self.get_project_agent(actor, personal_space_id, None, SYSTEM_HR_AGENT_ID)
42
39
 
43
- async def _ensure_system_project_hr_identity(self, personal_space_id):
44
- profile = ProjectAgentProfile(
45
- name='Bole',
46
- occupation_emoji='🔎',
47
- responsibility=(
48
- '维护 Agent 人员分布、当前工作与可审计招募记录,'
49
- '并在需要新角色时执行受策略约束的招募。'
50
- ),
51
- agent_type='hr',
52
- work_kinds=['agent-recruitment', 'staffing-review'],
53
- capabilities=[
54
- 'Agent 招募', '人员分布', '工作状态', '招募审计',
55
- 'fuli.employee:bole',
56
- ],
57
- initial_preferences=[
58
- '每次招募保留任务、原因、触发来源与时间线。',
59
- '只展示 Provider 已记录的人员状态,不推测未上报的工作。',
60
- ],
61
- status='active',
62
- )
63
- updated_at = now_utc()
64
- node_id = stable_uuid(
65
- self.settings.provider_id,
66
- personal_space_id,
67
- 'project-agent',
68
- SYSTEM_HR_AGENT_ID,
69
- )
70
- await self.runtime.driver.execute_query(
71
- '''
72
- MATCH (space:FuliSpace {id: $personal_space_id, kind: 'personal'})
73
- MERGE (agent:FuliProjectAgent {id: $id})
74
- ON CREATE SET agent.agent_id = $agent_id,
75
- agent.created_at = $updated_at,
76
- agent.system_managed = true
77
- ON CREATE SET agent.profile_json = $profile_json,
78
- agent.name = $name,
79
- agent.occupation_emoji = $occupation_emoji,
80
- agent.responsibility = $responsibility,
81
- agent.capabilities = $capabilities,
82
- agent.work_kinds = $work_kinds,
83
- agent.agent_type = 'hr',
84
- agent.memory_scope = 'reviewed_agent',
85
- agent.status = 'active',
86
- agent.updated_at = $updated_at
87
- MERGE (space)-[:HAS_PROJECT_AGENT_IDENTITY]->(agent)
88
- RETURN agent
89
- ''',
90
- personal_space_id=personal_space_id,
91
- id=node_id,
92
- agent_id=SYSTEM_HR_AGENT_ID,
93
- profile_json=profile.model_dump_json(),
94
- name=profile.name,
95
- occupation_emoji=profile.occupation_emoji,
96
- responsibility=profile.responsibility,
97
- capabilities=profile.capabilities,
98
- work_kinds=profile.work_kinds,
99
- updated_at=updated_at,
100
- )
101
-
102
40
  async def ensure_system_project_coordinator(
103
41
  self,
104
42
  actor: dict,
105
43
  personal_space_id: str,
106
44
  ) -> ProjectAgentRecord:
107
- self._require_personal()
108
- await self.authorize(actor, personal_space_id, 'maintainer')
109
- profile = ProjectAgentProfile(
110
- name='项目协调人',
111
- responsibility='评估任务、选择模型策略、路由已有 Agent,并审计职责缺口。',
112
- agent_type='coordinator',
113
- work_kinds=['task-coordination'],
114
- capabilities=['任务评估', '模型策略', 'Agent 路由'],
115
- initial_preferences=['质量与可验收完成优先于成本和时间'],
116
- status='active',
117
- )
118
- updated_at = now_utc()
119
- node_id = stable_uuid(
120
- self.settings.provider_id,
121
- personal_space_id,
122
- 'project-agent',
123
- SYSTEM_COORDINATOR_AGENT_ID,
124
- )
125
- await self.runtime.driver.execute_query(
126
- '''
127
- MATCH (space:FuliSpace {id: $personal_space_id, kind: 'personal'})
128
- MERGE (agent:FuliProjectAgent {id: $id})
129
- ON CREATE SET agent.agent_id = $agent_id,
130
- agent.created_at = $updated_at,
131
- agent.system_managed = true
132
- ON CREATE SET agent.profile_json = $profile_json,
133
- agent.name = $name,
134
- agent.occupation_emoji = $occupation_emoji,
135
- agent.responsibility = $responsibility,
136
- agent.capabilities = $capabilities,
137
- agent.work_kinds = $work_kinds,
138
- agent.agent_type = 'coordinator',
139
- agent.memory_scope = 'reviewed_agent',
140
- agent.status = 'active',
141
- agent.updated_at = $updated_at
142
- MERGE (space)-[:HAS_PROJECT_AGENT_IDENTITY]->(agent)
143
- RETURN agent
144
- ''',
145
- personal_space_id=personal_space_id,
146
- id=node_id,
147
- agent_id=SYSTEM_COORDINATOR_AGENT_ID,
148
- profile_json=profile.model_dump_json(),
149
- name=profile.name,
150
- occupation_emoji=profile.occupation_emoji,
151
- responsibility=profile.responsibility,
152
- capabilities=profile.capabilities,
153
- work_kinds=profile.work_kinds,
154
- updated_at=updated_at,
155
- )
45
+ await self._ensure_system_agents(actor, personal_space_id)
156
46
  return await self.get_project_agent(
157
- actor,
158
- personal_space_id,
159
- None,
160
- SYSTEM_COORDINATOR_AGENT_ID,
47
+ actor, personal_space_id, None, SYSTEM_COORDINATOR_AGENT_ID,
161
48
  )
162
49
 
50
+ async def _ensure_system_agents(self, actor, personal_space_id):
51
+ self._require_personal()
52
+ await self.authorize(actor, personal_space_id, 'maintainer')
53
+ async with query_store_transaction(self) as scoped:
54
+ await ensure_system_agents(
55
+ scoped.runtime.driver, scoped.settings.provider_id, personal_space_id,
56
+ )
57
+ await merge_legacy_hr(scoped, personal_space_id)
58
+
163
59
  async def upsert_project_agent(
164
60
  self,
165
61
  actor: dict,
@@ -207,10 +103,13 @@ class StoreProjectAgents:
207
103
  status_code=422,
208
104
  detail='the system HR identity cannot change Agent type',
209
105
  )
210
- if request.agent_id == SYSTEM_HR_AGENT_ID and request.profile.status != 'active':
106
+ if (
107
+ request.agent_id in (SYSTEM_HR_AGENT_ID, SYSTEM_COORDINATOR_AGENT_ID)
108
+ and request.profile.status != 'active'
109
+ ):
211
110
  raise HTTPException(
212
111
  status_code=422,
213
- detail='the system HR must remain active',
112
+ detail='the project manager and HR must remain active',
214
113
  )
215
114
  space = await self.authorize(actor, request.personal_space_id, 'maintainer')
216
115
  if request.personal_project_id:
@@ -488,7 +387,7 @@ class StoreProjectAgents:
488
387
  if agent_id == SYSTEM_COORDINATOR_AGENT_ID:
489
388
  raise HTTPException(
490
389
  status_code=422,
491
- detail='the system coordinator cannot be archived',
390
+ detail='the project manager cannot be archived',
492
391
  )
493
392
  if agent_id == SYSTEM_HR_AGENT_ID:
494
393
  raise HTTPException(
@@ -571,6 +470,8 @@ class StoreProjectAgents:
571
470
  [:HAS_PROJECT_AGENT_IDENTITY]->(agent:FuliProjectAgent)
572
471
  WHERE ($agent_id IS NULL OR agent.agent_id = $agent_id)
573
472
  AND ($status IS NULL OR agent.status = $status)
473
+ // A retired system identity stays readable by ID for history only.
474
+ AND ($agent_id IS NOT NULL OR agent.superseded_by IS NULL)
574
475
  AND (
575
476
  $personal_project_id IS NULL
576
477
  OR EXISTS {
@@ -99,6 +99,32 @@ class StoreTaskContexts:
99
99
  id=self._task_session_id(space_id, source_application, session_id))
100
100
  return json.loads(rows[0]['record_json']) if rows else None
101
101
 
102
+ async def recent_agent_sessions(self, actor, space_id, agent_id, project_id, limit):
103
+ """The host conversations an Agent most recently worked in, newest first."""
104
+ self._require_personal()
105
+ await self.authorize(actor, space_id, 'reader')
106
+ rows, _, _ = await self.runtime.driver.execute_query(
107
+ '''
108
+ MATCH (:FuliSpace {id: $space_id, kind: 'personal'})-
109
+ [:HAS_TASK_CONTEXT_SESSION]->(:FuliTaskContextSession)-
110
+ [:HAS_CONTEXT]->(task:FuliTaskContext {project_agent_id: $agent_id})
111
+ WHERE $project_id IS NULL OR task.personal_project_id = $project_id
112
+ RETURN task.record_json AS record_json, task.created_at AS created_at
113
+ ORDER BY task.created_at DESC LIMIT 200
114
+ ''', space_id=space_id, agent_id=agent_id, project_id=project_id, routing_='r',
115
+ )
116
+ sessions = {}
117
+ for row in rows:
118
+ record = json.loads(row['record_json'])
119
+ key = (record['source_application'], record['session_id'])
120
+ if key not in sessions:
121
+ sessions[key] = {
122
+ 'source_application': key[0], 'session_id': key[1],
123
+ 'personal_project_id': record.get('personal_project_id'),
124
+ 'last_active': str(row['created_at']),
125
+ }
126
+ return list(sessions.values())[:limit]
127
+
102
128
  async def get_task_context(self, actor, space_id, token, source_application):
103
129
  self._require_personal()
104
130
  await self.authorize(actor, space_id, 'reader')
@@ -0,0 +1,128 @@
1
+ """The two fixed roles every personal space has: Jefa (project manager) and Bole (HR).
2
+
3
+ Jefa also coordinates task routing, replacing the earlier separate coordinator
4
+ identity. That legacy node is retired, not deleted, so old tasks keep their
5
+ original coordinator reference.
6
+ """
7
+
8
+ import json
9
+
10
+ from .project_agent_models import ProjectAgentProfile
11
+ from .provider_values import now_utc, stable_uuid
12
+
13
+ SYSTEM_COORDINATOR_AGENT_ID = 'employee.jefa'
14
+ LEGACY_COORDINATOR_AGENT_ID = 'fuli-project-coordinator'
15
+ SYSTEM_HR_AGENT_ID = 'employee.bole'
16
+
17
+
18
+ def coordinator_profile() -> ProjectAgentProfile:
19
+ return ProjectAgentProfile(
20
+ name='Jefa',
21
+ occupation_emoji='🧭',
22
+ responsibility='项目经理:拆解目标、维护看板与进展,并为任务选择合适的 Agent。',
23
+ agent_type='coordinator',
24
+ work_kinds=['project_management', 'planning', 'review', 'task-coordination'],
25
+ capabilities=['项目规划', '任务拆解', '看板读写', 'Agent 路由', 'fuli.employee:jefa'],
26
+ initial_preferences=[
27
+ '质量与可验收完成优先于成本和时间。',
28
+ 'Agent 认为完成时只移入待确认,最终完成由用户决定。',
29
+ ],
30
+ status='active',
31
+ )
32
+
33
+
34
+ def hr_profile() -> ProjectAgentProfile:
35
+ return ProjectAgentProfile(
36
+ name='Bole',
37
+ occupation_emoji='🔎',
38
+ responsibility='HR:维护 Agent 人员分布与工作状态,按需招募并保留招募记录。',
39
+ agent_type='hr',
40
+ work_kinds=['agent-recruitment', 'staffing-review'],
41
+ capabilities=['Agent 招募', '人员分布', '工作状态', '招募审计', 'fuli.employee:bole'],
42
+ initial_preferences=[
43
+ '每次招募保留任务、原因、触发来源与时间线。',
44
+ '只展示 Provider 已记录的人员状态,不推测未上报的工作。',
45
+ ],
46
+ status='active',
47
+ )
48
+
49
+
50
+ SYSTEM_PROFILES = {
51
+ SYSTEM_COORDINATOR_AGENT_ID: coordinator_profile,
52
+ SYSTEM_HR_AGENT_ID: hr_profile,
53
+ }
54
+
55
+
56
+ async def ensure_system_identity(driver, provider_id, space_id, agent_id):
57
+ """Create the fixed role once; an existing identity keeps the user's edits.
58
+
59
+ A Jefa recruited before it became a fixed role is adopted in place, keeping
60
+ its profile, assignments, memory and history.
61
+ """
62
+ default = SYSTEM_PROFILES[agent_id]()
63
+ node_id = stable_uuid(provider_id, space_id, 'project-agent', agent_id)
64
+ rows, _, _ = await driver.execute_query(
65
+ 'MATCH (agent:FuliProjectAgent {id: $id}) RETURN agent.profile_json AS profile_json',
66
+ id=node_id,
67
+ )
68
+ profile = default
69
+ if rows and rows[0]['profile_json']:
70
+ stored = json.loads(rows[0]['profile_json'])
71
+ profile = ProjectAgentProfile.model_validate(
72
+ {**stored, 'agent_type': default.agent_type, 'status': 'active'}
73
+ )
74
+ now = now_utc()
75
+ await driver.execute_query(
76
+ '''
77
+ MATCH (space:FuliSpace {id: $space_id, kind: 'personal'})
78
+ MERGE (agent:FuliProjectAgent {id: $id})
79
+ ON CREATE SET agent.agent_id = $agent_id,
80
+ agent.name = $name,
81
+ agent.occupation_emoji = $occupation_emoji,
82
+ agent.responsibility = $responsibility,
83
+ agent.capabilities = $capabilities,
84
+ agent.work_kinds = $work_kinds,
85
+ agent.memory_scope = 'reviewed_agent',
86
+ agent.created_at = $now,
87
+ agent.updated_at = $now
88
+ SET agent.profile_json = $profile_json,
89
+ agent.agent_type = $agent_type,
90
+ agent.status = 'active',
91
+ agent.system_managed = true
92
+ MERGE (space)-[:HAS_PROJECT_AGENT_IDENTITY]->(agent)
93
+ ''',
94
+ space_id=space_id,
95
+ id=node_id,
96
+ agent_id=agent_id,
97
+ profile_json=profile.model_dump_json(),
98
+ name=profile.name,
99
+ occupation_emoji=profile.occupation_emoji,
100
+ responsibility=profile.responsibility,
101
+ capabilities=profile.capabilities,
102
+ work_kinds=profile.work_kinds,
103
+ agent_type=profile.agent_type,
104
+ now=now,
105
+ )
106
+
107
+
108
+ async def retire_legacy_coordinator(driver, space_id):
109
+ await driver.execute_query(
110
+ '''
111
+ MATCH (:FuliSpace {id: $space_id, kind: 'personal'})-[:HAS_PROJECT_AGENT_IDENTITY]->
112
+ (agent:FuliProjectAgent {agent_id: $legacy_id})
113
+ WHERE agent.status <> 'archived'
114
+ SET agent.status = 'archived',
115
+ agent.superseded_by = $canonical_id,
116
+ agent.updated_at = $now
117
+ ''',
118
+ space_id=space_id,
119
+ legacy_id=LEGACY_COORDINATOR_AGENT_ID,
120
+ canonical_id=SYSTEM_COORDINATOR_AGENT_ID,
121
+ now=now_utc(),
122
+ )
123
+
124
+
125
+ async def ensure_system_agents(driver, provider_id, space_id):
126
+ for agent_id in SYSTEM_PROFILES:
127
+ await ensure_system_identity(driver, provider_id, space_id, agent_id)
128
+ await retire_legacy_coordinator(driver, space_id)
@@ -12,8 +12,8 @@ from fastapi import HTTPException
12
12
 
13
13
  from .project_agent_models import ProjectAgentProfile
14
14
  from .provider_values import now_utc
15
+ from .system_agents import SYSTEM_HR_AGENT_ID
15
16
 
16
- SYSTEM_HR_AGENT_ID = 'employee.bole'
17
17
  LEGACY_HR_AGENT_ID = 'fuli-project-hr'
18
18
  REFERENCE_FIELDS = (
19
19
  'agent_id', 'hr_agent_id', 'lead_agent_id', 'coordinator_agent_id',
@@ -7,6 +7,16 @@ from .task_context_models import TaskContextBegin, TaskContextCheckpoint, TaskCo
7
7
 
8
8
 
9
9
  def register_task_context_routes(application, store, Actor):
10
+ @application.get('/v1/task-context-sessions/recent')
11
+ async def recent_sessions(actor: Actor,
12
+ personal_space_id: Annotated[str, Query(min_length=1, max_length=128)],
13
+ project_agent_id: Annotated[str, Query(min_length=1, max_length=128)],
14
+ personal_project_id: Annotated[str | None, Query(min_length=1, max_length=128)] = None,
15
+ limit: Annotated[int, Query(ge=1, le=20)] = 5):
16
+ return await store.recent_agent_sessions(
17
+ actor, personal_space_id, project_agent_id, personal_project_id, limit,
18
+ )
19
+
10
20
  @application.get('/v1/task-context-sessions/current')
11
21
  async def current_context(actor: Actor,
12
22
  personal_space_id: Annotated[str, Query(min_length=1, max_length=128)],
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fuli-graph-provider"
3
- version = "0.9.0"
3
+ version = "0.11.0"
4
4
  description = "Graphiti-backed personal and shared project provider for Fuli."
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "fuli-context",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "fuli-context",
9
- "version": "0.9.0",
9
+ "version": "0.11.0",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@a2a-js/sdk": "1.3.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fuli-context",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "Local-first collaboration relationship graph for AI agents.",
5
5
  "type": "module",
6
6
  "files": [
@@ -14,9 +14,7 @@
14
14
  "docs/agent-conversations-and-collaboration.md",
15
15
  "docs/employee-agents.md",
16
16
  "docs/agent-interface-architecture.md",
17
- "docs/roundtable-beta-testing.md",
18
- "docs/roundtable-beta-validation.md",
19
- "docs/roundtable-research.md",
17
+ "docs/agent-roundtable.md",
20
18
  "examples/external-knowledge/markdown-folder.mjs",
21
19
  "graph-provider/Dockerfile",
22
20
  "graph-provider/pyproject.toml",
@@ -288,7 +288,7 @@ worker session (`workerRuntime.sessionId`, or a worker session link only when su
288
288
  Label `sourceApplication` and `sourceSessionId` separately as the reporting host. They do not
289
289
  identify an external worker; do not substitute them for missing worker evidence.
290
290
  Use only the source-labelled cumulative `tokenUsage` reported for that worker. If it is absent,
291
- show `not reported`; never estimate it or copy a conversation-wide total into a worker row. If no
291
+ show `-`; never estimate it or copy a conversation-wide total into a worker row. If no
292
292
  worker session link exists, show `workerRuntime.sessionId` or `not reported`; never invent a link. This applies only
293
293
  to Provider-reported actual workers. When empty `executionSummary` is returned, omit rows; an absent
294
294
  `executionSummary` also omits rows. Configured, allowed, or available clients/executors are not evidence
@@ -0,0 +1,14 @@
1
+ import { sendJson } from '../http/response.js';
2
+
3
+ // Read-only console view of Agent-to-Agent threads.
4
+ export async function handleRoundtableApiRequest({ request, response, url, roundtable }) {
5
+ if (!roundtable || request.method !== 'GET') return false;
6
+ if (url.pathname === '/api/roundtable/threads') {
7
+ sendJson(response, 200, roundtable.threads({ limit: Math.min(Number(url.searchParams.get('limit')) || 50, 200) }));
8
+ return true;
9
+ }
10
+ const match = /^\/api\/roundtable\/threads\/([^/]+)$/.exec(url.pathname);
11
+ if (!match) return false;
12
+ sendJson(response, 200, roundtable.thread({ threadId: decodeURIComponent(match[1]) }));
13
+ return true;
14
+ }