fuli-context 0.10.0 → 0.11.1
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/README.md +27 -30
- package/README.zh-CN.md +22 -26
- package/dist/web/assets/AboutPage-cW9hDghN.js +1 -0
- package/dist/web/assets/AgentConversations-CGznTWuV.js +2 -0
- package/dist/web/assets/AgentConversations-XqNEBY_2.css +1 -0
- package/dist/web/assets/AgentHand-DkyF4vgk.js +1 -0
- package/dist/web/assets/AgentName-C1mAVr-v.js +1 -0
- package/dist/web/assets/AgentProfilePage-pKUR0rfj.js +1 -0
- package/dist/web/assets/AgentsDirectoryPage-CUlbT2hD.js +1 -0
- package/dist/web/assets/AgentsDirectoryPage-yRWktiin.css +1 -0
- package/dist/web/assets/ConnectionsPage-CM9C-zEI.css +1 -0
- package/dist/web/assets/ConnectionsPage-CuDHIJXu.js +1 -0
- package/dist/web/assets/EmployeeRecruitDialog-CVbqCzHb.css +1 -0
- package/dist/web/assets/EmployeeRecruitDialog-D9QfGipd.js +1 -0
- package/dist/web/assets/EmployeeWorkbenchPage-D-c_Jm2z.css +1 -0
- package/dist/web/assets/EmployeeWorkbenchPage-DAtvGney.js +3 -0
- package/dist/web/assets/KnowledgeEditDialog-a-5rasG0.js +1 -0
- package/dist/web/assets/KnowledgeEditDialog-hT9znkoe.css +1 -0
- package/dist/web/assets/KnowledgeInspector-BGsqjNY2.js +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-BndgUsHS.js +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-DeL7Jp34.css +1 -0
- package/dist/web/assets/KnowledgePage-CQweO0YJ.js +1 -0
- package/dist/web/assets/KnowledgeWorkspace-BtUjKTLb.js +4 -0
- package/dist/web/assets/KnowledgeWorkspace-D4a7FcAb.css +1 -0
- package/dist/web/assets/NotFoundPage-BmQTTqQn.js +1 -0
- package/dist/web/assets/OverviewPage-By-fUzSG.css +1 -0
- package/dist/web/assets/OverviewPage-C73HyTsM.js +1 -0
- package/dist/web/assets/PersonalProfilePage-BMuOrppx.js +1 -0
- package/dist/web/assets/PersonalProfilePage-DrMDAAdy.css +1 -0
- package/dist/web/assets/PersonalProjectsPage-Cq9_kFvs.js +1 -0
- package/dist/web/assets/ProjectAgentFirstTask-Bi1ZsTC1.css +1 -0
- package/dist/web/assets/ProjectAgentFirstTask-CaHvOjkp.js +1 -0
- package/dist/web/assets/ProjectAgentsPage-DWGkgIsr.css +1 -0
- package/dist/web/assets/ProjectAgentsPage-WVcNdzVi.js +12 -0
- package/dist/web/assets/ProjectScopePicker-zAgiaZKL.js +1 -0
- package/dist/web/assets/PublicProjectsPage-DKamXDV9.css +1 -0
- package/dist/web/assets/PublicProjectsPage-DL093f65.js +1 -0
- package/dist/web/assets/ReviewPage-D_NIhPx8.js +1 -0
- package/dist/web/assets/RoundtablesPage-Cb0R4GaM.css +1 -0
- package/dist/web/assets/RoundtablesPage-CdVVyZE-.js +1 -0
- package/dist/web/assets/SettingsPage-BmYkLul4.css +1 -0
- package/dist/web/assets/SettingsPage-CbLaau8Q.js +1 -0
- package/dist/web/assets/SharedJefaReportPage-aygd6wvb.js +1 -0
- package/dist/web/assets/UiDialog-Q7fsJUoO.js +1 -0
- package/dist/web/assets/UiDisclosure-BS-muK5j.js +1 -0
- package/dist/web/assets/UiSegmented-CT28bxqw.js +1 -0
- package/dist/web/assets/UiSelect-Bv6R0yr1.css +1 -0
- package/dist/web/assets/UiSelect-DcGpPQQo.js +1 -0
- package/dist/web/assets/WritingTastePage-B_zYD1df.css +1 -0
- package/dist/web/assets/WritingTastePage-CYlwAFqV.js +1 -0
- package/dist/web/assets/agent-profile-BZj3ezWe.css +1 -0
- package/dist/web/assets/{attention-store-Dk-_P4Oi.js → attention-store-D-qIFRIb.js} +1 -1
- package/dist/web/assets/{catalog-DN72452f.js → catalog-UDv854JN.js} +1 -1
- package/dist/web/assets/client-DLS5FZBI.js +1 -0
- package/dist/web/assets/{console-BtPqo79j.js → console-CqzwSo7a.js} +1 -1
- package/dist/web/assets/{conversation-launcher-settings-jkHRmWai.js → conversation-launcher-settings-DRoIz42V.js} +1 -1
- package/dist/web/assets/{i18n-D2CJkEYK.js → i18n-b14l2pQA.js} +3 -3
- package/dist/web/assets/{identity-B3zEw-1u.js → identity-BWbPmZZf.js} +1 -1
- package/dist/web/assets/index-D1OHIz4c.js +5 -0
- package/dist/web/assets/index-kPB0Cf8Q.css +1 -0
- package/dist/web/assets/{model-S7ehAjky.js → model-DupZy8Qb.js} +1 -1
- package/dist/web/assets/{personal-profile-graph--GG3Mizo.js → personal-profile-graph-BPTtMJTZ.js} +1 -1
- package/dist/web/assets/shared-report-BYnLhmCZ.js +1 -0
- package/dist/web/assets/{useAgentRoster-BAPKWZ9y.js → useAgentRoster-ep7SpKv1.js} +1 -1
- package/dist/web/assets/{useApi-CROJJdhE-BtFMXFjw.js → useApi-CROJJdhE-CEQAEuug.js} +1 -1
- package/dist/web/assets/{vue-router-DkaTtPn0.js → vue-router-B6ZjK-Br.js} +1 -1
- package/dist/web/index.html +14 -16
- package/docs/agent-conversations-and-collaboration.md +9 -9
- package/docs/agent-roundtable.md +77 -0
- package/docs/employee-agents.md +2 -2
- package/docs/project-agent-memory.md +1 -1
- package/graph-provider/fuli_graph/agent_conversation_models.py +11 -2
- package/graph-provider/fuli_graph/conversation_context.py +40 -9
- package/graph-provider/fuli_graph/default_project_lead.py +88 -0
- package/graph-provider/fuli_graph/project_agent_context_models.py +5 -1
- package/graph-provider/fuli_graph/project_agent_delegations.py +118 -0
- package/graph-provider/fuli_graph/project_agent_employee_numbers.py +41 -0
- package/graph-provider/fuli_graph/project_agent_memory_routes.py +20 -0
- package/graph-provider/fuli_graph/project_agent_models.py +1 -0
- package/graph-provider/fuli_graph/project_agent_task_models.py +3 -0
- package/graph-provider/fuli_graph/project_agent_team_growth.py +2 -2
- package/graph-provider/fuli_graph/runtime.py +10 -76
- package/graph-provider/fuli_graph/store.py +2 -0
- package/graph-provider/fuli_graph/store_agent_conversations.py +60 -8
- package/graph-provider/fuli_graph/store_project_agent_context.py +52 -4
- package/graph-provider/fuli_graph/store_project_agent_coordination_policy.py +16 -1
- package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +3 -1
- package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +3 -2
- package/graph-provider/fuli_graph/store_project_agent_tasks.py +12 -8
- package/graph-provider/fuli_graph/store_project_agents.py +56 -129
- package/graph-provider/fuli_graph/store_task_contexts.py +26 -0
- package/graph-provider/fuli_graph/system_agents.py +130 -0
- package/graph-provider/fuli_graph/system_hr_identity.py +1 -1
- package/graph-provider/fuli_graph/task_context_routes.py +10 -0
- package/graph-provider/pyproject.toml +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -2
- package/src/agent-roundtable/http.js +14 -0
- package/src/agent-roundtable/service.js +247 -0
- package/src/agent-roundtable/store.js +86 -0
- package/src/agent-roundtable/tools.js +61 -0
- package/src/agent-roundtable/wake.js +100 -0
- package/src/agent-tools/agent-conversation-definitions.js +3 -3
- package/src/agent-tools/graph-definitions.js +3 -2
- package/src/agent-tools/interface-catalog.js +0 -5
- package/src/agent-tools.js +2 -2
- package/src/agents/claude-code/wake.js +37 -0
- package/src/agents/codex/wake.js +47 -0
- package/src/agents/identity-receipt.js +35 -7
- package/src/agents/session-log.js +34 -0
- package/src/agents/wake-registry.js +14 -0
- package/src/cli/command-registry.js +0 -2
- package/src/cli/setup-command.js +2 -5
- package/src/cli/update-command.js +13 -2
- package/src/cli.js +0 -5
- package/src/employees/management-policy.js +4 -2
- package/src/employees/manifest.js +3 -0
- package/src/employees/recruitment.js +3 -2
- package/src/graphiti/agent-conversations.js +11 -9
- package/src/graphiti/agent-knowledge-workflows.js +34 -7
- package/src/graphiti/federated-application.js +17 -5
- package/src/graphiti/project-agent-mapping.js +2 -0
- package/src/graphiti/project-agent-task-entry.js +38 -8
- package/src/graphiti/project-agent-team-context.js +4 -1
- package/src/graphiti/project-path-context.js +7 -2
- package/src/graphiti/provider-client.js +17 -0
- package/src/graphiti/repository-project-registration.js +17 -0
- package/src/graphiti/task-entry-preferences.js +17 -4
- package/src/graphiti/task-entry-project-scope.js +67 -0
- package/src/http/api-router.js +5 -3
- package/src/http/error-mapping.js +0 -2
- package/src/mcp/instructions.js +5 -5
- package/src/mcp/session-id.js +1 -1
- package/src/mcp/tool-annotations.js +3 -4
- package/src/setup/agents.js +1 -41
- package/src/setup/graph-runtime.js +15 -2
- package/src/setup/session-skill.js +22 -2
- package/dist/web/assets/AboutPage-D-GyJNv1.js +0 -1
- package/dist/web/assets/AgentConversations-DOuZgUeA.js +0 -2
- package/dist/web/assets/AgentConversations-DS8h3yKn.css +0 -1
- package/dist/web/assets/AgentHand-eGsXoGSq.js +0 -1
- package/dist/web/assets/AgentName-Dtmu9Ff5.js +0 -1
- package/dist/web/assets/AgentProfilePage-D8Tvayn7.js +0 -1
- package/dist/web/assets/AgentsDirectoryPage-BTEfKZjF.js +0 -1
- package/dist/web/assets/ConnectionsPage-C1w99yN3.css +0 -1
- package/dist/web/assets/ConnectionsPage-DwOIcvl9.js +0 -1
- package/dist/web/assets/EmployeeRecruitDialog-BrIMRblq.css +0 -1
- package/dist/web/assets/EmployeeRecruitDialog-D11blfwU.js +0 -1
- package/dist/web/assets/EmployeeWorkbenchPage-BcEzYt65.css +0 -1
- package/dist/web/assets/EmployeeWorkbenchPage-CK2BAoGt.js +0 -3
- package/dist/web/assets/GrowthLoading-KTjE2e50.js +0 -1
- package/dist/web/assets/KnowledgeEditDialog-B3x8vs3c.css +0 -1
- package/dist/web/assets/KnowledgeEditDialog-n9trHQ5d.js +0 -1
- package/dist/web/assets/KnowledgeInspector-BcNjgw9H.js +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-BzgxP-nG.css +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-DMBpGxLr.js +0 -1
- package/dist/web/assets/KnowledgePage-POkCMW8f.js +0 -1
- package/dist/web/assets/KnowledgeWorkspace-2FvQadhB.js +0 -4
- package/dist/web/assets/KnowledgeWorkspace-sb8OJ97I.css +0 -1
- package/dist/web/assets/NotFoundPage-BCMZbeB8.js +0 -1
- package/dist/web/assets/OverviewPage-0CFWCG9-.css +0 -1
- package/dist/web/assets/OverviewPage-0mYifpjp.js +0 -1
- package/dist/web/assets/PersonalProfilePage-Chv2a2pi.css +0 -1
- package/dist/web/assets/PersonalProfilePage-JW6Muix6.js +0 -1
- package/dist/web/assets/PersonalProjectsPage-CRbi64JP.js +0 -1
- package/dist/web/assets/ProjectAgentFirstTask-6afJ8HUe.css +0 -1
- package/dist/web/assets/ProjectAgentFirstTask-DJ0wTbh8.js +0 -1
- package/dist/web/assets/ProjectAgentsPage-CPgKVLgg.js +0 -12
- package/dist/web/assets/ProjectAgentsPage-DeuRkMp1.css +0 -1
- package/dist/web/assets/ProjectScopePicker-BwB7gK-7.js +0 -1
- package/dist/web/assets/PublicProjectsPage-CB272Sq-.js +0 -1
- package/dist/web/assets/ReviewPage-CelTZU4C.js +0 -1
- package/dist/web/assets/RoundtablesPage-C9dXfWCl.js +0 -2
- package/dist/web/assets/RoundtablesPage-DCbIBnBq.css +0 -1
- package/dist/web/assets/SearchableSelect-f7EHbyDL.js +0 -1
- package/dist/web/assets/SettingsPage-5g7mRN2F.css +0 -1
- package/dist/web/assets/SettingsPage-nAijmm5f.js +0 -1
- package/dist/web/assets/SharedJefaReportPage-CjpJ9bkk.js +0 -1
- package/dist/web/assets/UiDisclosure-CWlC91Zt.js +0 -1
- package/dist/web/assets/WritingTastePage-DaHWY61P.js +0 -1
- package/dist/web/assets/WritingTastePage-SMGF39mf.css +0 -1
- package/dist/web/assets/agent-profile-CBvJHGjt.css +0 -1
- package/dist/web/assets/client-B2cRl-7c.js +0 -1
- package/dist/web/assets/copy-D2LqwtPZ.js +0 -1
- package/dist/web/assets/index-CNp0tRNp.css +0 -1
- package/dist/web/assets/index-ulISYH02.js +0 -5
- package/dist/web/assets/shared-report-DEK9wvhl.js +0 -1
- package/dist/web/assets/useModalDialog-D8tiJfbc.js +0 -1
- package/docs/roundtable.md +0 -46
- package/src/agents/a2a/roundtable-participant.js +0 -374
- package/src/agents/claude-code/roundtable-participant.js +0 -24
- package/src/agents/codex/roundtable-config.js +0 -32
- package/src/agents/codex/roundtable-participant.js +0 -34
- package/src/agents/grok/roundtable-participant.js +0 -185
- package/src/agents/pi/roundtable-participant.js +0 -193
- package/src/agents/pi/roundtable-result-format.js +0 -183
- package/src/cli/roundtable-command.js +0 -79
- package/src/roundtables/application.js +0 -69
- package/src/roundtables/domain.js +0 -181
- package/src/roundtables/http-router.js +0 -58
- package/src/roundtables/mcp-server.js +0 -55
- package/src/roundtables/participant-registry.js +0 -12
- package/src/roundtables/process.js +0 -70
- package/src/roundtables/profiles.js +0 -84
- package/src/roundtables/result-contract.js +0 -47
- package/src/roundtables/server.js +0 -52
- package/src/roundtables/service.js +0 -480
- package/src/roundtables/store.js +0 -63
- package/src/roundtables/tool-contract.js +0 -58
- package/src/roundtables/turn-prompt.js +0 -24
- package/src/roundtables/worker.js +0 -85
- /package/dist/web/assets/{GrowthLoading-CIuQ1icy.css → client-CIuQ1icy.css} +0 -0
|
@@ -5,6 +5,9 @@ from fastapi import HTTPException
|
|
|
5
5
|
|
|
6
6
|
from .personal_project_access import authorize_personal_project
|
|
7
7
|
from .project_agent_access import authorize_project_agent
|
|
8
|
+
from .project_agent_employee_numbers import (
|
|
9
|
+
ASSIGN_EMPLOYEE_NUMBERS, EMPLOYEE_NUMBER_LOCK, employee_number_label,
|
|
10
|
+
)
|
|
8
11
|
from .project_agent_models import (
|
|
9
12
|
ProjectAgentAssignmentCreate,
|
|
10
13
|
ProjectAgentAssignmentEnd,
|
|
@@ -20,146 +23,42 @@ from .project_agent_models import (
|
|
|
20
23
|
from .provider_values import native_datetime, now_utc, stable_uuid
|
|
21
24
|
from .project_agent_names import agent_display_name
|
|
22
25
|
from .store_transactions import query_store_transaction
|
|
26
|
+
from .system_agents import (
|
|
27
|
+
SYSTEM_COORDINATOR_AGENT_ID,
|
|
28
|
+
SYSTEM_HR_AGENT_ID,
|
|
29
|
+
ensure_system_agents,
|
|
30
|
+
)
|
|
23
31
|
from .system_hr_identity import LEGACY_HR_AGENT_ID, merge_legacy_hr, resolve_hr_alias
|
|
24
32
|
|
|
25
33
|
|
|
26
|
-
SYSTEM_COORDINATOR_AGENT_ID = 'fuli-project-coordinator'
|
|
27
|
-
SYSTEM_HR_AGENT_ID = 'employee.bole'
|
|
28
|
-
|
|
29
|
-
|
|
30
34
|
class StoreProjectAgents:
|
|
31
35
|
async def ensure_system_project_hr(
|
|
32
36
|
self,
|
|
33
37
|
actor: dict,
|
|
34
38
|
personal_space_id: str,
|
|
35
39
|
) -> ProjectAgentRecord:
|
|
36
|
-
self.
|
|
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)
|
|
40
|
+
await self._ensure_system_agents(actor, personal_space_id)
|
|
41
41
|
return await self.get_project_agent(actor, personal_space_id, None, SYSTEM_HR_AGENT_ID)
|
|
42
42
|
|
|
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
43
|
async def ensure_system_project_coordinator(
|
|
103
44
|
self,
|
|
104
45
|
actor: dict,
|
|
105
46
|
personal_space_id: str,
|
|
106
47
|
) -> ProjectAgentRecord:
|
|
107
|
-
self.
|
|
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
|
-
)
|
|
48
|
+
await self._ensure_system_agents(actor, personal_space_id)
|
|
156
49
|
return await self.get_project_agent(
|
|
157
|
-
actor,
|
|
158
|
-
personal_space_id,
|
|
159
|
-
None,
|
|
160
|
-
SYSTEM_COORDINATOR_AGENT_ID,
|
|
50
|
+
actor, personal_space_id, None, SYSTEM_COORDINATOR_AGENT_ID,
|
|
161
51
|
)
|
|
162
52
|
|
|
53
|
+
async def _ensure_system_agents(self, actor, personal_space_id):
|
|
54
|
+
self._require_personal()
|
|
55
|
+
await self.authorize(actor, personal_space_id, 'maintainer')
|
|
56
|
+
async with query_store_transaction(self) as scoped:
|
|
57
|
+
await ensure_system_agents(
|
|
58
|
+
scoped.runtime.driver, scoped.settings.provider_id, personal_space_id,
|
|
59
|
+
)
|
|
60
|
+
await merge_legacy_hr(scoped, personal_space_id)
|
|
61
|
+
|
|
163
62
|
async def upsert_project_agent(
|
|
164
63
|
self,
|
|
165
64
|
actor: dict,
|
|
@@ -207,10 +106,13 @@ class StoreProjectAgents:
|
|
|
207
106
|
status_code=422,
|
|
208
107
|
detail='the system HR identity cannot change Agent type',
|
|
209
108
|
)
|
|
210
|
-
if
|
|
109
|
+
if (
|
|
110
|
+
request.agent_id in (SYSTEM_HR_AGENT_ID, SYSTEM_COORDINATOR_AGENT_ID)
|
|
111
|
+
and request.profile.status != 'active'
|
|
112
|
+
):
|
|
211
113
|
raise HTTPException(
|
|
212
114
|
status_code=422,
|
|
213
|
-
detail='the
|
|
115
|
+
detail='the project manager and HR must remain active',
|
|
214
116
|
)
|
|
215
117
|
space = await self.authorize(actor, request.personal_space_id, 'maintainer')
|
|
216
118
|
if request.personal_project_id:
|
|
@@ -235,6 +137,7 @@ class StoreProjectAgents:
|
|
|
235
137
|
records, _, _ = await self.runtime.driver.execute_query(
|
|
236
138
|
'''
|
|
237
139
|
MATCH (space:FuliSpace {id: $personal_space_id, kind: 'personal'})
|
|
140
|
+
''' + EMPLOYEE_NUMBER_LOCK + '''
|
|
238
141
|
MERGE (agent:FuliProjectAgent {id: $id})
|
|
239
142
|
ON CREATE SET agent.agent_id = $agent_id,
|
|
240
143
|
agent.created_at = $updated_at,
|
|
@@ -244,7 +147,12 @@ class StoreProjectAgents:
|
|
|
244
147
|
SET agent._profile_upsert_lock = true
|
|
245
148
|
REMOVE agent._profile_upsert_lock
|
|
246
149
|
WITH space, agent
|
|
247
|
-
WHERE agent.status IS NULL OR agent.status <> 'archived'
|
|
150
|
+
WHERE (agent.status IS NULL OR agent.status <> 'archived')
|
|
151
|
+
AND ($status = 'active' OR NOT EXISTS {
|
|
152
|
+
MATCH (space)-[:CONTAINS_PROJECT]->(:FuliPersonalProject)-
|
|
153
|
+
[:HAS_PROJECT_AGENT_COORDINATION_POLICY]->(policy:FuliProjectAgentCoordinationPolicy)
|
|
154
|
+
WHERE policy.team_lead_agent_id = agent.agent_id
|
|
155
|
+
})
|
|
248
156
|
SET agent.profile_json = $profile_json,
|
|
249
157
|
agent.name = $name,
|
|
250
158
|
agent.occupation_emoji = $occupation_emoji,
|
|
@@ -260,6 +168,7 @@ class StoreProjectAgents:
|
|
|
260
168
|
agent.recruitment_id, $recruitment_id),
|
|
261
169
|
agent.updated_at = $updated_at
|
|
262
170
|
MERGE (space)-[:HAS_PROJECT_AGENT_IDENTITY]->(agent)
|
|
171
|
+
''' + ASSIGN_EMPLOYEE_NUMBERS + '''
|
|
263
172
|
RETURN agent
|
|
264
173
|
''',
|
|
265
174
|
personal_space_id=request.personal_space_id,
|
|
@@ -283,7 +192,7 @@ class StoreProjectAgents:
|
|
|
283
192
|
raise HTTPException(
|
|
284
193
|
status_code=409,
|
|
285
194
|
detail=(
|
|
286
|
-
'archived
|
|
195
|
+
'archived Agents require a dedicated restore operation; project leads must hand off before deactivation'
|
|
287
196
|
),
|
|
288
197
|
)
|
|
289
198
|
if request.personal_project_id:
|
|
@@ -488,7 +397,7 @@ class StoreProjectAgents:
|
|
|
488
397
|
if agent_id == SYSTEM_COORDINATOR_AGENT_ID:
|
|
489
398
|
raise HTTPException(
|
|
490
399
|
status_code=422,
|
|
491
|
-
detail='the
|
|
400
|
+
detail='the project manager cannot be archived',
|
|
492
401
|
)
|
|
493
402
|
if agent_id == SYSTEM_HR_AGENT_ID:
|
|
494
403
|
raise HTTPException(
|
|
@@ -521,7 +430,12 @@ class StoreProjectAgents:
|
|
|
521
430
|
'awaiting_recruitment', 'queued', 'running',
|
|
522
431
|
'paused', 'blocked', 'awaiting_review'
|
|
523
432
|
]] AS open_tasks
|
|
524
|
-
WHERE agent.status = 'archived' OR size(open_tasks) = 0
|
|
433
|
+
WHERE (agent.status = 'archived' OR size(open_tasks) = 0)
|
|
434
|
+
AND NOT EXISTS {
|
|
435
|
+
MATCH (space)-[:CONTAINS_PROJECT]->(:FuliPersonalProject)-
|
|
436
|
+
[:HAS_PROJECT_AGENT_COORDINATION_POLICY]->(policy:FuliProjectAgentCoordinationPolicy)
|
|
437
|
+
WHERE policy.team_lead_agent_id = agent.agent_id
|
|
438
|
+
}
|
|
525
439
|
SET agent.status = 'archived',
|
|
526
440
|
agent.archive_reason = coalesce(agent.archive_reason, $reason),
|
|
527
441
|
agent.archived_at = coalesce(agent.archived_at, $updated_at),
|
|
@@ -547,7 +461,7 @@ class StoreProjectAgents:
|
|
|
547
461
|
if not records:
|
|
548
462
|
raise HTTPException(
|
|
549
463
|
status_code=409,
|
|
550
|
-
detail='Agent is missing
|
|
464
|
+
detail='Agent is missing, has non-terminal task work, or is a project lead; hand off before archiving',
|
|
551
465
|
)
|
|
552
466
|
return await self.get_project_agent(
|
|
553
467
|
actor,
|
|
@@ -571,6 +485,8 @@ class StoreProjectAgents:
|
|
|
571
485
|
[:HAS_PROJECT_AGENT_IDENTITY]->(agent:FuliProjectAgent)
|
|
572
486
|
WHERE ($agent_id IS NULL OR agent.agent_id = $agent_id)
|
|
573
487
|
AND ($status IS NULL OR agent.status = $status)
|
|
488
|
+
// A retired system identity stays readable by ID for history only.
|
|
489
|
+
AND ($agent_id IS NOT NULL OR agent.superseded_by IS NULL)
|
|
574
490
|
AND (
|
|
575
491
|
$personal_project_id IS NULL
|
|
576
492
|
OR EXISTS {
|
|
@@ -867,6 +783,11 @@ class StoreProjectAgents:
|
|
|
867
783
|
WITH project, assignment, agent
|
|
868
784
|
WHERE assignment.status = 'active'
|
|
869
785
|
AND coalesce(assignment.revision, 0) = $expected_revision
|
|
786
|
+
AND NOT EXISTS {
|
|
787
|
+
MATCH (project)-[:HAS_PROJECT_AGENT_COORDINATION_POLICY]->
|
|
788
|
+
(policy:FuliProjectAgentCoordinationPolicy)
|
|
789
|
+
WHERE policy.team_lead_agent_id = agent.agent_id
|
|
790
|
+
}
|
|
870
791
|
SET assignment.status = 'ended',
|
|
871
792
|
assignment.end_reason = $reason,
|
|
872
793
|
assignment.ended_at = $updated_at,
|
|
@@ -885,7 +806,7 @@ class StoreProjectAgents:
|
|
|
885
806
|
if not records:
|
|
886
807
|
raise HTTPException(
|
|
887
808
|
status_code=409,
|
|
888
|
-
detail='assignment is not active
|
|
809
|
+
detail='assignment is not active, revision is stale, or project leadership requires a handoff',
|
|
889
810
|
)
|
|
890
811
|
row = records[0]
|
|
891
812
|
return self._assignment(
|
|
@@ -950,6 +871,11 @@ class StoreProjectAgents:
|
|
|
950
871
|
REMOVE lifecycle_agent._task_lifecycle_lock
|
|
951
872
|
WITH project, ended, ended_agent, replacement_agent,
|
|
952
873
|
collect(lifecycle_agent) AS locked_agents
|
|
874
|
+
WHERE ended.status <> 'active' OR NOT EXISTS {
|
|
875
|
+
MATCH (project)-[:HAS_PROJECT_AGENT_COORDINATION_POLICY]->
|
|
876
|
+
(policy:FuliProjectAgentCoordinationPolicy)
|
|
877
|
+
WHERE policy.team_lead_agent_id = ended_agent.agent_id
|
|
878
|
+
}
|
|
953
879
|
OPTIONAL MATCH (existing_replacement:FuliProjectAgentAssignment {
|
|
954
880
|
id: $replacement_id
|
|
955
881
|
})
|
|
@@ -1174,6 +1100,7 @@ class StoreProjectAgents:
|
|
|
1174
1100
|
})
|
|
1175
1101
|
return ProjectAgentRecord(
|
|
1176
1102
|
agent_id=raw['agent_id'],
|
|
1103
|
+
employee_number=employee_number_label(raw.get('employee_number')),
|
|
1177
1104
|
legacy_agent_ids=list(raw.get('legacy_agent_ids') or []),
|
|
1178
1105
|
personal_space_id=personal_space_id,
|
|
1179
1106
|
personal_project_id=projection_project_id,
|
|
@@ -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,130 @@
|
|
|
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 .project_agent_employee_numbers import ASSIGN_EMPLOYEE_NUMBERS, EMPLOYEE_NUMBER_LOCK
|
|
12
|
+
from .provider_values import now_utc, stable_uuid
|
|
13
|
+
|
|
14
|
+
SYSTEM_COORDINATOR_AGENT_ID = 'employee.jefa'
|
|
15
|
+
LEGACY_COORDINATOR_AGENT_ID = 'fuli-project-coordinator'
|
|
16
|
+
SYSTEM_HR_AGENT_ID = 'employee.bole'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def coordinator_profile() -> ProjectAgentProfile:
|
|
20
|
+
return ProjectAgentProfile(
|
|
21
|
+
name='Jefa',
|
|
22
|
+
occupation_emoji='🧭',
|
|
23
|
+
responsibility='项目经理:拆解目标、维护看板与进展,并为任务选择合适的 Agent。',
|
|
24
|
+
agent_type='coordinator',
|
|
25
|
+
work_kinds=['project_management', 'planning', 'review', 'task-coordination'],
|
|
26
|
+
capabilities=['项目规划', '任务拆解', '看板读写', 'Agent 路由', 'fuli.employee:jefa'],
|
|
27
|
+
initial_preferences=[
|
|
28
|
+
'质量与可验收完成优先于成本和时间。',
|
|
29
|
+
'Agent 认为完成时只移入待确认,最终完成由用户决定。',
|
|
30
|
+
],
|
|
31
|
+
status='active',
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def hr_profile() -> ProjectAgentProfile:
|
|
36
|
+
return ProjectAgentProfile(
|
|
37
|
+
name='Bole',
|
|
38
|
+
occupation_emoji='🔎',
|
|
39
|
+
responsibility='HR:维护 Agent 人员分布与工作状态,按需招募并保留招募记录。',
|
|
40
|
+
agent_type='hr',
|
|
41
|
+
work_kinds=['agent-recruitment', 'staffing-review'],
|
|
42
|
+
capabilities=['Agent 招募', '人员分布', '工作状态', '招募审计', 'fuli.employee:bole'],
|
|
43
|
+
initial_preferences=[
|
|
44
|
+
'每次招募保留任务、原因、触发来源与时间线。',
|
|
45
|
+
'只展示 Provider 已记录的人员状态,不推测未上报的工作。',
|
|
46
|
+
],
|
|
47
|
+
status='active',
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
SYSTEM_PROFILES = {
|
|
52
|
+
SYSTEM_COORDINATOR_AGENT_ID: coordinator_profile,
|
|
53
|
+
SYSTEM_HR_AGENT_ID: hr_profile,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def ensure_system_identity(driver, provider_id, space_id, agent_id):
|
|
58
|
+
"""Create the fixed role once; an existing identity keeps the user's edits.
|
|
59
|
+
|
|
60
|
+
A Jefa recruited before it became a fixed role is adopted in place, keeping
|
|
61
|
+
its profile, assignments, memory and history.
|
|
62
|
+
"""
|
|
63
|
+
default = SYSTEM_PROFILES[agent_id]()
|
|
64
|
+
node_id = stable_uuid(provider_id, space_id, 'project-agent', agent_id)
|
|
65
|
+
rows, _, _ = await driver.execute_query(
|
|
66
|
+
'MATCH (agent:FuliProjectAgent {id: $id}) RETURN agent.profile_json AS profile_json',
|
|
67
|
+
id=node_id,
|
|
68
|
+
)
|
|
69
|
+
profile = default
|
|
70
|
+
if rows and rows[0]['profile_json']:
|
|
71
|
+
stored = json.loads(rows[0]['profile_json'])
|
|
72
|
+
profile = ProjectAgentProfile.model_validate(
|
|
73
|
+
{**stored, 'agent_type': default.agent_type, 'status': 'active'}
|
|
74
|
+
)
|
|
75
|
+
now = now_utc()
|
|
76
|
+
await driver.execute_query(
|
|
77
|
+
'''
|
|
78
|
+
MATCH (space:FuliSpace {id: $space_id, kind: 'personal'})
|
|
79
|
+
''' + EMPLOYEE_NUMBER_LOCK + '''
|
|
80
|
+
MERGE (agent:FuliProjectAgent {id: $id})
|
|
81
|
+
ON CREATE SET agent.agent_id = $agent_id,
|
|
82
|
+
agent.name = $name,
|
|
83
|
+
agent.occupation_emoji = $occupation_emoji,
|
|
84
|
+
agent.responsibility = $responsibility,
|
|
85
|
+
agent.capabilities = $capabilities,
|
|
86
|
+
agent.work_kinds = $work_kinds,
|
|
87
|
+
agent.memory_scope = 'reviewed_agent',
|
|
88
|
+
agent.created_at = $now,
|
|
89
|
+
agent.updated_at = $now
|
|
90
|
+
SET agent.profile_json = $profile_json,
|
|
91
|
+
agent.agent_type = $agent_type,
|
|
92
|
+
agent.status = 'active',
|
|
93
|
+
agent.system_managed = true
|
|
94
|
+
MERGE (space)-[:HAS_PROJECT_AGENT_IDENTITY]->(agent)
|
|
95
|
+
''' + ASSIGN_EMPLOYEE_NUMBERS + ' RETURN agent',
|
|
96
|
+
space_id=space_id,
|
|
97
|
+
id=node_id,
|
|
98
|
+
agent_id=agent_id,
|
|
99
|
+
profile_json=profile.model_dump_json(),
|
|
100
|
+
name=profile.name,
|
|
101
|
+
occupation_emoji=profile.occupation_emoji,
|
|
102
|
+
responsibility=profile.responsibility,
|
|
103
|
+
capabilities=profile.capabilities,
|
|
104
|
+
work_kinds=profile.work_kinds,
|
|
105
|
+
agent_type=profile.agent_type,
|
|
106
|
+
now=now,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
async def retire_legacy_coordinator(driver, space_id):
|
|
111
|
+
await driver.execute_query(
|
|
112
|
+
'''
|
|
113
|
+
MATCH (:FuliSpace {id: $space_id, kind: 'personal'})-[:HAS_PROJECT_AGENT_IDENTITY]->
|
|
114
|
+
(agent:FuliProjectAgent {agent_id: $legacy_id})
|
|
115
|
+
WHERE agent.status <> 'archived'
|
|
116
|
+
SET agent.status = 'archived',
|
|
117
|
+
agent.superseded_by = $canonical_id,
|
|
118
|
+
agent.updated_at = $now
|
|
119
|
+
''',
|
|
120
|
+
space_id=space_id,
|
|
121
|
+
legacy_id=LEGACY_COORDINATOR_AGENT_ID,
|
|
122
|
+
canonical_id=SYSTEM_COORDINATOR_AGENT_ID,
|
|
123
|
+
now=now_utc(),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
async def ensure_system_agents(driver, provider_id, space_id):
|
|
128
|
+
for agent_id in SYSTEM_PROFILES:
|
|
129
|
+
await ensure_system_identity(driver, provider_id, space_id, agent_id)
|
|
130
|
+
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)],
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fuli-context",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "fuli-context",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.11.1",
|
|
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.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Local-first collaboration relationship graph for AI agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -14,7 +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.md",
|
|
17
|
+
"docs/agent-roundtable.md",
|
|
18
18
|
"examples/external-knowledge/markdown-folder.mjs",
|
|
19
19
|
"graph-provider/Dockerfile",
|
|
20
20
|
"graph-provider/pyproject.toml",
|
|
@@ -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
|
+
}
|