fuli-context 0.11.1 → 0.12.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 +6 -5
- package/README.zh-CN.md +5 -4
- package/dist/web/assets/AboutPage-Dized8Uz.js +1 -0
- package/dist/web/assets/AgentConversations-Ccy41mEA.js +2 -0
- package/dist/web/assets/AgentHand-UzVhbKUC.js +1 -0
- package/dist/web/assets/AgentName-DJpvkub2.js +1 -0
- package/dist/web/assets/AgentProfilePage-CFViQ8kj.js +1 -0
- package/dist/web/assets/AgentsDirectoryPage-CBFTg_IE.js +1 -0
- package/dist/web/assets/AgentsDirectoryPage-Cv-vxw2C.css +1 -0
- package/dist/web/assets/ConnectionsPage-uXcabab_.js +1 -0
- package/dist/web/assets/EmployeeRecruitDialog-D8Hvhc-M.js +1 -0
- package/dist/web/assets/EmployeeWorkbenchPage-BQes4L1f.css +1 -0
- package/dist/web/assets/EmployeeWorkbenchPage-TBl_HNj6.js +3 -0
- package/dist/web/assets/KnowledgeEditDialog-HmiqLC-Y.js +1 -0
- package/dist/web/assets/KnowledgeInspector-Cj2yK4Qh.js +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-DT3BROZm.js +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-ccgscrPd.css +1 -0
- package/dist/web/assets/KnowledgePage-C4J4VueL.js +1 -0
- package/dist/web/assets/KnowledgeWorkspace-DA_dqVRG.js +4 -0
- package/dist/web/assets/NotFoundPage-x_Yd59mm.js +1 -0
- package/dist/web/assets/OverviewPage-3eZjQ26v.js +1 -0
- package/dist/web/assets/OverviewPage-8LBN8Shi.css +1 -0
- package/dist/web/assets/PersonalProfilePage-CDQk096L.js +1 -0
- package/dist/web/assets/PersonalProjectsPage-CJxp4Wyz.js +1 -0
- package/dist/web/assets/ProjectAgentFirstTask-BkRJ0zwO.js +1 -0
- package/dist/web/assets/ProjectAgentsPage-BuNNqSEy.js +12 -0
- package/dist/web/assets/ProjectScopePicker-CiDxM2Yx.js +1 -0
- package/dist/web/assets/PublicProjectsPage-ByOT559r.js +1 -0
- package/dist/web/assets/ReviewPage-C0Vdr9uq.js +1 -0
- package/dist/web/assets/RoundtablesPage-Drou-B81.css +1 -0
- package/dist/web/assets/RoundtablesPage-L3ShEWS3.js +1 -0
- package/dist/web/assets/SettingsPage-BE8pWbI6.css +1 -0
- package/dist/web/assets/SettingsPage-McYuinGz.js +1 -0
- package/dist/web/assets/SharedJefaReportPage-Bg6lIk3E.js +1 -0
- package/dist/web/assets/UiButton-x8hiHNRM.js +1 -0
- package/dist/web/assets/UiDialog-mjletK0y.js +1 -0
- package/dist/web/assets/UiDisclosure-_IxeiexW.js +1 -0
- package/dist/web/assets/UiSegmented-WFfz9Evl.js +1 -0
- package/dist/web/assets/UiSelect-K29jnV-1.js +1 -0
- package/dist/web/assets/WritingTastePage-DCULB-d2.js +1 -0
- package/dist/web/assets/agent-pins-DS4mNsNt.js +1 -0
- package/dist/web/assets/agent-profile-Bh3MJGvb.css +1 -0
- package/dist/web/assets/agent-work-summary-C-yW7x7A.js +1 -0
- package/dist/web/assets/{attention-store-D-qIFRIb.js → attention-store-CA5y8j1R.js} +1 -1
- package/dist/web/assets/catalog-D0VEfOAV.js +1 -0
- package/dist/web/assets/client-DnwnUBQV.js +1 -0
- package/dist/web/assets/console-Cw7TzOSC.js +1 -0
- package/dist/web/assets/conversation-launcher-settings-Yc13viwr.js +1 -0
- package/dist/web/assets/{i18n-b14l2pQA.js → i18n-oJFlt8hW.js} +3 -3
- package/dist/web/assets/{identity-BWbPmZZf.js → identity-DSGS4CZc.js} +1 -1
- package/dist/web/assets/index-BfHCcUL2.css +1 -0
- package/dist/web/assets/index-DO10_fLc.js +5 -0
- package/dist/web/assets/judgment-types-DR931cZ7.js +1 -0
- package/dist/web/assets/model-BJ-UF4cs.js +1 -0
- package/dist/web/assets/{personal-profile-graph-BPTtMJTZ.js → personal-profile-graph-SOK0BR0w.js} +1 -1
- package/dist/web/assets/project-team-api-C_0qJk_B.js +1 -0
- package/dist/web/assets/shared-report-CiX64ZiD.js +1 -0
- package/dist/web/assets/{agent-work-summary-LeW-Al_S.js → task-evidence-DTiPCs7r.js} +1 -1
- package/dist/web/assets/useAgentRoster-B84mzZIg.js +1 -0
- package/dist/web/assets/{useApi-CROJJdhE-CEQAEuug.js → useApi-CROJJdhE-7gt0QyQ5.js} +1 -1
- package/dist/web/assets/vue-router-CCvPGaLD.js +1 -0
- package/dist/web/index.html +17 -12
- package/dist/web/licenses/ant-design-icons.txt +22 -0
- package/docs/agent-conversations-and-collaboration.md +3 -3
- package/docs/agent-roundtable.md +33 -0
- package/docs/client-connections.md +86 -0
- package/docs/employee-agents.md +3 -3
- package/docs/judgment-and-autonomy.md +33 -0
- package/docs/project-agent-memory.md +6 -6
- package/graph-provider/fuli_graph/ai_review.py +154 -0
- package/graph-provider/fuli_graph/common_knowledge.py +12 -0
- package/graph-provider/fuli_graph/default_project_lead.py +1 -1
- package/graph-provider/fuli_graph/graph_models.py +14 -0
- package/graph-provider/fuli_graph/graph_query.py +217 -0
- package/graph-provider/fuli_graph/knowledge_audit.py +6 -0
- package/graph-provider/fuli_graph/knowledge_batch_confirmation.py +6 -0
- package/graph-provider/fuli_graph/knowledge_management.py +23 -0
- package/graph-provider/fuli_graph/knowledge_review.py +383 -4
- package/graph-provider/fuli_graph/knowledge_review_models.py +61 -2
- package/graph-provider/fuli_graph/personal_global_preferences.py +5 -1
- package/graph-provider/fuli_graph/preference_conflicts.py +91 -1
- package/graph-provider/fuli_graph/project_agent_delegations.py +70 -2
- package/graph-provider/fuli_graph/project_agent_memory_routes.py +18 -1
- package/graph-provider/fuli_graph/project_agent_remote_origins.py +154 -0
- package/graph-provider/fuli_graph/project_agent_task_models.py +3 -1
- package/graph-provider/fuli_graph/project_knowledge.py +41 -0
- package/graph-provider/fuli_graph/runtime.py +15 -0
- package/graph-provider/fuli_graph/store_agent_verification.py +12 -5
- package/graph-provider/fuli_graph/store_knowledge.py +98 -58
- package/graph-provider/fuli_graph/store_project_agent_attention.py +1 -1
- package/graph-provider/fuli_graph/store_project_agent_context.py +3 -2
- package/graph-provider/fuli_graph/store_project_agent_coordination_policy.py +1 -1
- package/graph-provider/fuli_graph/store_project_agent_task_activity.py +26 -0
- package/graph-provider/fuli_graph/store_project_agent_task_reads.py +1 -1
- package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +5 -5
- package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +4 -3
- package/graph-provider/fuli_graph/store_project_agent_tasks.py +2 -2
- package/graph-provider/fuli_graph/store_project_agents.py +6 -6
- package/graph-provider/fuli_graph/system_agents.py +31 -2
- package/graph-provider/fuli_graph/system_hr_identity.py +21 -7
- package/graph-provider/pyproject.toml +2 -1
- package/npm-shrinkwrap.json +280 -5
- package/package.json +7 -2
- package/skills/operating-fuli/SKILL.md +3 -3
- package/src/agent-peer/coordinator-http.js +117 -0
- package/src/agent-peer/coordinator-service.js +258 -0
- package/src/agent-peer/coordinator-store.js +95 -0
- package/src/agent-peer/device-identity.js +70 -0
- package/src/agent-peer/http.js +47 -0
- package/src/agent-peer/node-store.js +127 -0
- package/src/agent-peer/origin-proof.js +56 -0
- package/src/agent-peer/peer-port.js +140 -0
- package/src/agent-peer/peer-runtime.js +298 -0
- package/src/agent-peer/peer-transport.js +78 -0
- package/src/agent-peer/protocol.js +161 -0
- package/src/agent-peer/runner.js +251 -0
- package/src/agent-roundtable/http.js +17 -3
- package/src/agent-roundtable/service.js +272 -43
- package/src/agent-roundtable/tools.js +15 -1
- package/src/agent-roundtable/wake.js +12 -3
- package/src/agent-tools/graph-definitions.js +16 -5
- package/src/agent-tools/graph-handlers.js +8 -0
- package/src/agent-tools/interface-catalog.js +16 -1
- package/src/agent-tools/judgment-definitions.js +12 -0
- package/src/agent-tools/project-agent-definitions.js +1 -1
- package/src/agents/claude-code/wake.js +7 -0
- package/src/agents/codex/wake.js +7 -0
- package/src/agents/identity-receipt.js +1 -1
- package/src/cli/command-registry.js +3 -0
- package/src/cli/connect-command.js +28 -0
- package/src/cli/mcp-command.js +99 -0
- package/src/cli/peer-command.js +100 -0
- package/src/cli/setup-agent-selection.js +2 -2
- package/src/cli/setup-command.js +7 -2
- package/src/cli.js +16 -0
- package/src/employees/catalog/bole.json +2 -2
- package/src/employees/catalog/tonborg.json +20 -0
- package/src/employees/manifest.js +2 -2
- package/src/employees/recruitment.js +20 -1
- package/src/graphiti/agent-collaboration.js +8 -2
- package/src/graphiti/federated-application.js +9 -1
- package/src/graphiti/knowledge-review.js +7 -3
- package/src/graphiti/project-agent-control-plane.js +7 -0
- package/src/graphiti/project-agent-mapping.js +2 -0
- package/src/graphiti/project-agent-task-entry.js +4 -4
- package/src/graphiti/project-agent-team-context.js +16 -18
- package/src/graphiti/project-team-view.js +82 -0
- package/src/graphiti/provider-client.js +9 -0
- package/src/graphiti/task-entry-preferences.js +6 -1
- package/src/http/api-router.js +12 -1
- package/src/http/graph-api-router.js +7 -0
- package/src/judgment/acceptance.js +91 -0
- package/src/judgment/engine.js +168 -0
- package/src/judgment/http.js +24 -0
- package/src/judgment/model.js +24 -0
- package/src/judgment/routing.js +96 -0
- package/src/judgment/scheduler.js +25 -0
- package/src/judgment/service.js +66 -0
- package/src/judgment/store.js +123 -0
- package/src/mcp/session-id.js +1 -0
- package/src/mcp/tool-annotations.js +8 -5
- package/src/mcp-server.js +2 -100
- package/src/server.js +22 -3
- package/src/setup/client-connection.js +16 -0
- package/src/system/system-service.js +5 -0
- package/dist/web/assets/AboutPage-cW9hDghN.js +0 -1
- package/dist/web/assets/AgentConversations-CGznTWuV.js +0 -2
- package/dist/web/assets/AgentHand-DkyF4vgk.js +0 -1
- package/dist/web/assets/AgentName-C1mAVr-v.js +0 -1
- package/dist/web/assets/AgentProfilePage-pKUR0rfj.js +0 -1
- package/dist/web/assets/AgentsDirectoryPage-CUlbT2hD.js +0 -1
- package/dist/web/assets/AgentsDirectoryPage-yRWktiin.css +0 -1
- package/dist/web/assets/ConnectionsPage-CuDHIJXu.js +0 -1
- package/dist/web/assets/EmployeeRecruitDialog-D9QfGipd.js +0 -1
- package/dist/web/assets/EmployeeWorkbenchPage-D-c_Jm2z.css +0 -1
- package/dist/web/assets/EmployeeWorkbenchPage-DAtvGney.js +0 -3
- package/dist/web/assets/KnowledgeEditDialog-a-5rasG0.js +0 -1
- package/dist/web/assets/KnowledgeInspector-BGsqjNY2.js +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-BndgUsHS.js +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-DeL7Jp34.css +0 -1
- package/dist/web/assets/KnowledgePage-CQweO0YJ.js +0 -1
- package/dist/web/assets/KnowledgeWorkspace-BtUjKTLb.js +0 -4
- package/dist/web/assets/NotFoundPage-BmQTTqQn.js +0 -1
- package/dist/web/assets/OverviewPage-By-fUzSG.css +0 -1
- package/dist/web/assets/OverviewPage-C73HyTsM.js +0 -1
- package/dist/web/assets/PersonalProfilePage-BMuOrppx.js +0 -1
- package/dist/web/assets/PersonalProjectsPage-Cq9_kFvs.js +0 -1
- package/dist/web/assets/ProjectAgentFirstTask-CaHvOjkp.js +0 -1
- package/dist/web/assets/ProjectAgentsPage-WVcNdzVi.js +0 -12
- package/dist/web/assets/ProjectScopePicker-zAgiaZKL.js +0 -1
- package/dist/web/assets/PublicProjectsPage-DL093f65.js +0 -1
- package/dist/web/assets/ReviewPage-D_NIhPx8.js +0 -1
- package/dist/web/assets/RoundtablesPage-Cb0R4GaM.css +0 -1
- package/dist/web/assets/RoundtablesPage-CdVVyZE-.js +0 -1
- package/dist/web/assets/SettingsPage-BmYkLul4.css +0 -1
- package/dist/web/assets/SettingsPage-CbLaau8Q.js +0 -1
- package/dist/web/assets/SharedJefaReportPage-aygd6wvb.js +0 -1
- package/dist/web/assets/UiDialog-Q7fsJUoO.js +0 -1
- package/dist/web/assets/UiDisclosure-BS-muK5j.js +0 -1
- package/dist/web/assets/UiSegmented-CT28bxqw.js +0 -1
- package/dist/web/assets/UiSelect-DcGpPQQo.js +0 -1
- package/dist/web/assets/WritingTastePage-CYlwAFqV.js +0 -1
- package/dist/web/assets/agent-profile-BZj3ezWe.css +0 -1
- package/dist/web/assets/catalog-UDv854JN.js +0 -1
- package/dist/web/assets/client-DLS5FZBI.js +0 -1
- package/dist/web/assets/console-CqzwSo7a.js +0 -1
- package/dist/web/assets/conversation-launcher-settings-DRoIz42V.js +0 -1
- package/dist/web/assets/index-D1OHIz4c.js +0 -5
- package/dist/web/assets/index-kPB0Cf8Q.css +0 -1
- package/dist/web/assets/model-DupZy8Qb.js +0 -1
- package/dist/web/assets/shared-report-BYnLhmCZ.js +0 -1
- package/dist/web/assets/useAgentRoster-ep7SpKv1.js +0 -1
- package/dist/web/assets/vue-router-B6ZjK-Br.js +0 -1
|
@@ -578,11 +578,25 @@ async def _upsert_reference(
|
|
|
578
578
|
space['id'], 'knowledge-reference', item_kind, item_id, project_id
|
|
579
579
|
)
|
|
580
580
|
changed_at = datetime.now(timezone.utc)
|
|
581
|
+
item_match = (
|
|
582
|
+
'MATCH (item:Entity {uuid: $item_id, group_id: $group_id})'
|
|
583
|
+
if item_kind == 'entity'
|
|
584
|
+
else (
|
|
585
|
+
'MATCH ()-[item:RELATES_TO '
|
|
586
|
+
'{uuid: $item_id, group_id: $group_id}]->()'
|
|
587
|
+
)
|
|
588
|
+
)
|
|
581
589
|
records, _, _ = await store.runtime.driver.execute_query(
|
|
582
590
|
'''
|
|
583
591
|
MATCH (space:FuliSpace {id: $space_id, kind: 'personal'})
|
|
584
592
|
MATCH (space)-[:CONTAINS_PROJECT]->
|
|
585
593
|
(target:FuliPersonalProject {project_id: $project_id})
|
|
594
|
+
'''
|
|
595
|
+
+ item_match
|
|
596
|
+
+ '''
|
|
597
|
+
SET item.fuli_ai_review_lock_version =
|
|
598
|
+
coalesce(item.fuli_ai_review_lock_version, 0) + 1
|
|
599
|
+
WITH space, target
|
|
586
600
|
MERGE (reference:FuliKnowledgeProjectReference {
|
|
587
601
|
space_id: $space_id,
|
|
588
602
|
item_kind: $item_kind,
|
|
@@ -604,6 +618,7 @@ async def _upsert_reference(
|
|
|
604
618
|
space_id=space['id'],
|
|
605
619
|
item_kind=item_kind,
|
|
606
620
|
item_id=item_id,
|
|
621
|
+
group_id=space['group_id'],
|
|
607
622
|
project_id=project_id,
|
|
608
623
|
reference_id=reference_id,
|
|
609
624
|
source_project_id=source_project_id,
|
|
@@ -635,6 +650,31 @@ async def _upsert_conflict(
|
|
|
635
650
|
records, _, _ = await store.runtime.driver.execute_query(
|
|
636
651
|
'''
|
|
637
652
|
MATCH (space:FuliSpace {id: $space_id, kind: 'personal'})
|
|
653
|
+
MATCH (item:Entity {uuid: $item_id, group_id: $group_id})
|
|
654
|
+
MATCH (target_item:Entity {
|
|
655
|
+
uuid: $target_item_id,
|
|
656
|
+
group_id: $group_id
|
|
657
|
+
})
|
|
658
|
+
WITH space,
|
|
659
|
+
CASE
|
|
660
|
+
WHEN item.uuid = target_item.uuid
|
|
661
|
+
THEN [{kind: 'entity', item: item}]
|
|
662
|
+
WHEN item.uuid < target_item.uuid
|
|
663
|
+
THEN [
|
|
664
|
+
{kind: 'entity', item: item},
|
|
665
|
+
{kind: 'entity', item: target_item}
|
|
666
|
+
]
|
|
667
|
+
ELSE [
|
|
668
|
+
{kind: 'entity', item: target_item},
|
|
669
|
+
{kind: 'entity', item: item}
|
|
670
|
+
]
|
|
671
|
+
END AS lock_items
|
|
672
|
+
UNWIND lock_items AS lock_item
|
|
673
|
+
WITH space, lock_item
|
|
674
|
+
ORDER BY lock_item.kind + ':' + lock_item.item.uuid
|
|
675
|
+
SET lock_item.item.fuli_ai_review_lock_version =
|
|
676
|
+
coalesce(lock_item.item.fuli_ai_review_lock_version, 0) + 1
|
|
677
|
+
WITH space, collect(lock_item) AS _locked_items
|
|
638
678
|
MERGE (conflict:FuliKnowledgeConflict {id: $conflict_id})
|
|
639
679
|
ON CREATE SET conflict.created_at = $changed_at
|
|
640
680
|
SET conflict.space_id = $space_id,
|
|
@@ -651,6 +691,7 @@ async def _upsert_conflict(
|
|
|
651
691
|
RETURN conflict
|
|
652
692
|
''',
|
|
653
693
|
space_id=space['id'],
|
|
694
|
+
group_id=space['group_id'],
|
|
654
695
|
conflict_id=conflict_id,
|
|
655
696
|
item_id=item_id,
|
|
656
697
|
target_item_id=target_item_id,
|
|
@@ -331,6 +331,13 @@ class GraphitiRuntime:
|
|
|
331
331
|
'FOR (n:FuliProjectAgentExecutorOutcomeReset) REQUIRE n.id IS UNIQUE',
|
|
332
332
|
'CREATE CONSTRAINT fuli_project_agent_executor_observation_id IF NOT EXISTS '
|
|
333
333
|
'FOR (n:FuliProjectAgentExecutorObservation) REQUIRE n.id IS UNIQUE',
|
|
334
|
+
'CREATE CONSTRAINT fuli_remote_origin_key_space IF NOT EXISTS '
|
|
335
|
+
'FOR (n:FuliRemoteOriginKey) REQUIRE n.space_id IS UNIQUE',
|
|
336
|
+
'CREATE CONSTRAINT fuli_remote_agent_origin_message IF NOT EXISTS '
|
|
337
|
+
'FOR (n:FuliRemoteAgentOrigin) REQUIRE (n.space_id, n.message_id) IS UNIQUE',
|
|
338
|
+
'CREATE CONSTRAINT fuli_remote_delegation_attempt IF NOT EXISTS '
|
|
339
|
+
'FOR (n:FuliRemoteDelegationAttempt) '
|
|
340
|
+
'REQUIRE (n.space_id, n.origin_node, n.origin_message_id, n.origin_attempt_id) IS UNIQUE',
|
|
334
341
|
):
|
|
335
342
|
await self.driver.execute_query(query)
|
|
336
343
|
for query in (
|
|
@@ -656,6 +663,10 @@ class GraphitiRuntime:
|
|
|
656
663
|
OR item.fuli_usage_generation IS NULL
|
|
657
664
|
OR item.fuli_inheritance_mode IS NULL
|
|
658
665
|
OR item.fuli_inherited_project_ids IS NULL
|
|
666
|
+
WITH item
|
|
667
|
+
ORDER BY 'entity:' + item.uuid
|
|
668
|
+
SET item.fuli_ai_review_lock_version =
|
|
669
|
+
coalesce(item.fuli_ai_review_lock_version, 0) + 1
|
|
659
670
|
SET item.fuli_utility_score =
|
|
660
671
|
coalesce(item.fuli_utility_score, 0.0),
|
|
661
672
|
item.fuli_confidence_score = coalesce(
|
|
@@ -688,6 +699,10 @@ class GraphitiRuntime:
|
|
|
688
699
|
OR item.fuli_usage_generation IS NULL
|
|
689
700
|
OR item.fuli_inheritance_mode IS NULL
|
|
690
701
|
OR item.fuli_inherited_project_ids IS NULL
|
|
702
|
+
WITH item
|
|
703
|
+
ORDER BY 'relationship:' + item.uuid
|
|
704
|
+
SET item.fuli_ai_review_lock_version =
|
|
705
|
+
coalesce(item.fuli_ai_review_lock_version, 0) + 1
|
|
691
706
|
SET item.fuli_utility_score =
|
|
692
707
|
coalesce(item.fuli_utility_score, 0.0),
|
|
693
708
|
item.fuli_confidence_score = coalesce(
|
|
@@ -43,7 +43,7 @@ class StoreAgentVerification:
|
|
|
43
43
|
task = await self._quality_task(actor, request)
|
|
44
44
|
rows, _, _ = await self.runtime.driver.execute_query(
|
|
45
45
|
'MATCH (t:FuliProjectAgentTask {task_id:$task, personal_space_id:$space})-[:HAS_VERIFICATION]->(v:FuliAgentVerification) '
|
|
46
|
-
'RETURN v.record_json AS record_json ORDER BY v.sequence', task=request.task_id, space=request.personal_space_id)
|
|
46
|
+
'RETURN v.record_json AS record_json, v.run_id AS run_id ORDER BY v.sequence', task=request.task_id, space=request.personal_space_id)
|
|
47
47
|
models = await self._quality_models(actor, request, task)
|
|
48
48
|
result = evaluate_quality_policy(complexity=task.get('complexity') or 'standard',
|
|
49
49
|
current_strategy=json.loads(task.get('effective_model_strategy_json') or '{"mode":"adaptive"}'),
|
|
@@ -53,7 +53,12 @@ class StoreAgentVerification:
|
|
|
53
53
|
if selected:
|
|
54
54
|
selected['executor_id'] = next(model['executor_id'] for model in models
|
|
55
55
|
if all(model.get(key) == selected[key] for key in ('provider', 'model', 'capability_tier', 'cost_rank')))
|
|
56
|
-
|
|
56
|
+
verified = next((row for row in rows if json.loads(row['record_json']).get('attempt_id') == result['verified_attempt_id']), None)
|
|
57
|
+
attempt = json.loads(verified['record_json']) if verified else None
|
|
58
|
+
if attempt:
|
|
59
|
+
attempt = {key: attempt.get(key) for key in ('attempt_id', 'artifact_revision', 'outcome', 'evidence_refs', 'verifier_id', 'executor_id', 'provider', 'model')}
|
|
60
|
+
attempt['run_id'] = verified.get('run_id')
|
|
61
|
+
return {**result, 'verified_attempt': attempt, 'evidence_authority': 'host_reported_test_evidence',
|
|
57
62
|
'execution_started': False, 'guidance': 'Only complete the task after passed for the current artifact revision. Blocked means no authorized preflighted model proves the required capability tier. Model selection does not prove execution.'}
|
|
58
63
|
|
|
59
64
|
async def record_agent_verification(self, actor, request):
|
|
@@ -92,7 +97,8 @@ class StoreAgentVerification:
|
|
|
92
97
|
raise HTTPException(409, 'An execution run cannot count as two verification attempts')
|
|
93
98
|
identifier = stable_uuid('verification', request.personal_space_id, request.task_id, request.attempt_id)
|
|
94
99
|
record = dict(attempt_id=request.attempt_id, outcome=request.outcome, artifact_revision=request.artifact_revision,
|
|
95
|
-
evidence_refs=request.evidence_refs, capability_tier=observed['capability_tier'], verifier_id=context['project_agent_id']
|
|
100
|
+
evidence_refs=request.evidence_refs, capability_tier=observed['capability_tier'], verifier_id=context['project_agent_id'],
|
|
101
|
+
executor_id=request.executor_id, provider=request.provider, model=request.model)
|
|
96
102
|
fingerprint = store._payload_hash(request)
|
|
97
103
|
rows, _, _ = await store.runtime.driver.execute_query(
|
|
98
104
|
'MATCH (t:FuliProjectAgentTask {task_id:$task, personal_space_id:$space}) '
|
|
@@ -106,6 +112,7 @@ class StoreAgentVerification:
|
|
|
106
112
|
result = await store.query_agent_quality(actor, request)
|
|
107
113
|
await store.runtime.driver.execute_query(
|
|
108
114
|
'MATCH (t:FuliProjectAgentTask {task_id:$task, personal_space_id:$space}) '
|
|
109
|
-
'SET t.quality_gate=$status, t.verified_artifact_revision=$revision',
|
|
110
|
-
task=request.task_id, space=request.personal_space_id, status=result['status'], revision=request.artifact_revision
|
|
115
|
+
'SET t.quality_gate=$status, t.verified_artifact_revision=$revision, t.verified_attempt_id=$attempt',
|
|
116
|
+
task=request.task_id, space=request.personal_space_id, status=result['status'], revision=request.artifact_revision,
|
|
117
|
+
attempt=result.get('verified_attempt_id'))
|
|
111
118
|
return result
|
|
@@ -325,6 +325,8 @@ class StoreKnowledge:
|
|
|
325
325
|
await self.runtime.driver.execute_query(
|
|
326
326
|
'''
|
|
327
327
|
UNWIND $entities AS row
|
|
328
|
+
WITH row
|
|
329
|
+
ORDER BY 'entity:' + row.uuid
|
|
328
330
|
MERGE (entity:Entity {uuid: row.uuid})
|
|
329
331
|
ON CREATE SET entity.group_id = $group_id,
|
|
330
332
|
entity.name = row.name,
|
|
@@ -365,6 +367,8 @@ class StoreKnowledge:
|
|
|
365
367
|
entity.fuli_preference_project_id = row.preference_project_id,
|
|
366
368
|
entity.fuli_preference_agent_id = row.preference_agent_id,
|
|
367
369
|
entity.fuli_attributes_json = row.attributes_json
|
|
370
|
+
SET entity.fuli_ai_review_lock_version =
|
|
371
|
+
coalesce(entity.fuli_ai_review_lock_version, 0) + 1
|
|
368
372
|
WITH count(entity) AS entity_count
|
|
369
373
|
CREATE (episode:Episodic {
|
|
370
374
|
uuid: $episode_id,
|
|
@@ -392,12 +396,95 @@ class StoreKnowledge:
|
|
|
392
396
|
})
|
|
393
397
|
WITH episode
|
|
394
398
|
UNWIND $entities AS row
|
|
399
|
+
WITH episode, row
|
|
400
|
+
ORDER BY 'entity:' + row.uuid
|
|
395
401
|
MATCH (entity:Entity {uuid: row.uuid})
|
|
396
402
|
MERGE (episode)-[mention:MENTIONS {uuid: row.mention_uuid}]->(entity)
|
|
397
403
|
SET mention.group_id = $group_id,
|
|
398
404
|
mention.created_at = $created_at
|
|
399
405
|
WITH episode
|
|
406
|
+
CALL {
|
|
407
|
+
WITH episode
|
|
408
|
+
OPTIONAL MATCH ()-[old:RELATES_TO {group_id: $group_id}]->()
|
|
409
|
+
WITH collect(old) AS candidate_olds
|
|
410
|
+
UNWIND candidate_olds AS old
|
|
411
|
+
WITH old
|
|
412
|
+
WHERE old IS NOT NULL
|
|
413
|
+
AND old.invalid_at IS NULL
|
|
414
|
+
AND NOT (old.uuid IN $relationship_ids)
|
|
415
|
+
AND (
|
|
416
|
+
EXISTS {
|
|
417
|
+
MATCH (:FuliSpace {id: $space_id})-
|
|
418
|
+
[:HAS_KNOWLEDGE_ASSIGNMENT]->
|
|
419
|
+
(assignment:FuliKnowledgeAssignment {
|
|
420
|
+
item_kind: 'relationship',
|
|
421
|
+
item_id: old.uuid
|
|
422
|
+
})
|
|
423
|
+
WHERE assignment.project_id = $personal_project_id
|
|
424
|
+
}
|
|
425
|
+
OR (
|
|
426
|
+
NOT EXISTS {
|
|
427
|
+
MATCH (:FuliSpace {id: $space_id})-
|
|
428
|
+
[:HAS_KNOWLEDGE_ASSIGNMENT]->
|
|
429
|
+
(:FuliKnowledgeAssignment {
|
|
430
|
+
item_kind: 'relationship',
|
|
431
|
+
item_id: old.uuid
|
|
432
|
+
})
|
|
433
|
+
}
|
|
434
|
+
AND EXISTS {
|
|
435
|
+
MATCH (old_episode:Episodic {group_id: $group_id})
|
|
436
|
+
WHERE old_episode.uuid IN coalesce(old.episodes, [])
|
|
437
|
+
AND (
|
|
438
|
+
old_episode.fuli_personal_project_id =
|
|
439
|
+
$personal_project_id
|
|
440
|
+
OR (
|
|
441
|
+
old_episode.fuli_personal_project_id IS NULL
|
|
442
|
+
AND $personal_project_id IS NULL
|
|
443
|
+
)
|
|
444
|
+
)
|
|
445
|
+
}
|
|
446
|
+
)
|
|
447
|
+
)
|
|
448
|
+
AND EXISTS {
|
|
449
|
+
MATCH (agent_episode:Episodic {group_id: $group_id})
|
|
450
|
+
WHERE agent_episode.uuid IN coalesce(old.episodes, [])
|
|
451
|
+
AND (
|
|
452
|
+
agent_episode.fuli_project_agent_id = $project_agent_id
|
|
453
|
+
OR (
|
|
454
|
+
agent_episode.fuli_project_agent_id IS NULL
|
|
455
|
+
AND $project_agent_id IS NULL
|
|
456
|
+
)
|
|
457
|
+
)
|
|
458
|
+
}
|
|
459
|
+
AND any(
|
|
460
|
+
replacement IN $superseded_relationships
|
|
461
|
+
WHERE replacement.key = old.fuli_key
|
|
462
|
+
)
|
|
463
|
+
WITH collect(old) AS superseded_edges
|
|
464
|
+
OPTIONAL MATCH ()-[existing:RELATES_TO {
|
|
465
|
+
group_id: $group_id
|
|
466
|
+
}]->()
|
|
467
|
+
WITH superseded_edges, collect(existing) AS candidate_existing
|
|
468
|
+
RETURN superseded_edges,
|
|
469
|
+
[edge IN candidate_existing
|
|
470
|
+
WHERE edge IS NOT NULL
|
|
471
|
+
AND edge.uuid IN $relationship_ids]
|
|
472
|
+
AS existing_edges
|
|
473
|
+
}
|
|
474
|
+
CALL {
|
|
475
|
+
WITH superseded_edges, existing_edges
|
|
476
|
+
UNWIND superseded_edges + existing_edges AS lock_edge
|
|
477
|
+
WITH lock_edge
|
|
478
|
+
WHERE lock_edge IS NOT NULL
|
|
479
|
+
ORDER BY 'relationship:' + lock_edge.uuid
|
|
480
|
+
SET lock_edge.fuli_ai_review_lock_version =
|
|
481
|
+
coalesce(lock_edge.fuli_ai_review_lock_version, 0) + 1
|
|
482
|
+
RETURN count(*) AS _locked_relationship_count
|
|
483
|
+
}
|
|
484
|
+
WITH episode, superseded_edges
|
|
400
485
|
UNWIND $relationships AS row
|
|
486
|
+
WITH episode, superseded_edges, row
|
|
487
|
+
ORDER BY 'relationship:' + row.uuid
|
|
401
488
|
MATCH (source:Entity {uuid: row.source_uuid})
|
|
402
489
|
MATCH (target:Entity {uuid: row.target_uuid})
|
|
403
490
|
MERGE (source)-[edge:RELATES_TO {uuid: row.uuid}]->(target)
|
|
@@ -435,73 +522,26 @@ class StoreKnowledge:
|
|
|
435
522
|
edge.fuli_workflow_confirmation_authority =
|
|
436
523
|
row.workflow_confirmation_authority,
|
|
437
524
|
edge.fuli_workflow_session_authority =
|
|
438
|
-
row.workflow_session_authority
|
|
525
|
+
row.workflow_session_authority,
|
|
526
|
+
edge.fuli_ai_review_lock_version = 1
|
|
439
527
|
SET edge.episodes =
|
|
440
528
|
CASE WHEN $episode_id IN coalesce(edge.episodes, [])
|
|
441
529
|
THEN edge.episodes
|
|
442
530
|
ELSE coalesce(edge.episodes, []) + $episode_id END
|
|
443
|
-
WITH count(edge) AS edge_count
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
EXISTS {
|
|
449
|
-
MATCH (:FuliSpace {id: $space_id})-
|
|
450
|
-
[:HAS_KNOWLEDGE_ASSIGNMENT]->
|
|
451
|
-
(assignment:FuliKnowledgeAssignment {
|
|
452
|
-
item_kind: 'relationship',
|
|
453
|
-
item_id: old.uuid
|
|
454
|
-
})
|
|
455
|
-
WHERE assignment.project_id = $personal_project_id
|
|
456
|
-
}
|
|
457
|
-
OR (
|
|
458
|
-
NOT EXISTS {
|
|
459
|
-
MATCH (:FuliSpace {id: $space_id})-
|
|
460
|
-
[:HAS_KNOWLEDGE_ASSIGNMENT]->
|
|
461
|
-
(:FuliKnowledgeAssignment {
|
|
462
|
-
item_kind: 'relationship',
|
|
463
|
-
item_id: old.uuid
|
|
464
|
-
})
|
|
465
|
-
}
|
|
466
|
-
AND EXISTS {
|
|
467
|
-
MATCH (old_episode:Episodic {group_id: $group_id})
|
|
468
|
-
WHERE old_episode.uuid IN coalesce(old.episodes, [])
|
|
469
|
-
AND (
|
|
470
|
-
old_episode.fuli_personal_project_id =
|
|
471
|
-
$personal_project_id
|
|
472
|
-
OR (
|
|
473
|
-
old_episode.fuli_personal_project_id IS NULL
|
|
474
|
-
AND $personal_project_id IS NULL
|
|
475
|
-
)
|
|
476
|
-
)
|
|
477
|
-
}
|
|
478
|
-
)
|
|
479
|
-
)
|
|
480
|
-
AND EXISTS {
|
|
481
|
-
MATCH (agent_episode:Episodic {group_id: $group_id})
|
|
482
|
-
WHERE agent_episode.uuid IN coalesce(old.episodes, [])
|
|
483
|
-
AND (
|
|
484
|
-
agent_episode.fuli_project_agent_id = $project_agent_id
|
|
485
|
-
OR (
|
|
486
|
-
agent_episode.fuli_project_agent_id IS NULL
|
|
487
|
-
AND $project_agent_id IS NULL
|
|
488
|
-
)
|
|
489
|
-
)
|
|
490
|
-
}
|
|
491
|
-
AND any(
|
|
531
|
+
WITH episode, superseded_edges, count(edge) AS edge_count
|
|
532
|
+
CALL {
|
|
533
|
+
WITH superseded_edges
|
|
534
|
+
UNWIND superseded_edges AS old
|
|
535
|
+
WITH old, head([
|
|
492
536
|
replacement IN $superseded_relationships
|
|
493
|
-
WHERE replacement.key = old.fuli_key
|
|
494
|
-
)
|
|
495
|
-
WITH edge_count, old, head([
|
|
496
|
-
replacement IN $superseded_relationships
|
|
497
|
-
WHERE replacement.key = old.fuli_key | replacement
|
|
498
|
-
]) AS replacement
|
|
499
|
-
FOREACH (_ IN CASE WHEN old IS NULL THEN [] ELSE [1] END |
|
|
537
|
+
WHERE replacement.key = old.fuli_key | replacement
|
|
538
|
+
]) AS replacement
|
|
500
539
|
SET old.invalid_at = $reference_time,
|
|
501
540
|
old.expired_at = $created_at,
|
|
502
541
|
old.fuli_replaced_by_item_id = replacement.replacement_id,
|
|
503
542
|
old.fuli_replaced_by_item_kind = 'relationship'
|
|
504
|
-
|
|
543
|
+
RETURN count(*) AS _superseded_relationship_count
|
|
544
|
+
}
|
|
505
545
|
RETURN edge_count
|
|
506
546
|
''',
|
|
507
547
|
entities=[
|
|
@@ -24,7 +24,7 @@ class StoreProjectAgentAttention:
|
|
|
24
24
|
self, actor, space, request.personal_project_id, agent_id,
|
|
25
25
|
require_active=True,
|
|
26
26
|
allow_unassigned=agent_id in {
|
|
27
|
-
'employee.bole', 'employee.jefa', 'fuli-project-coordinator',
|
|
27
|
+
'employee.bole', 'employee.jefa', 'employee.tonborg', 'fuli-project-coordinator',
|
|
28
28
|
},
|
|
29
29
|
)
|
|
30
30
|
if request.task_id:
|
|
@@ -5,9 +5,10 @@ from .project_agent_context_models import ProjectAgentContextResolution
|
|
|
5
5
|
from .project_agent_task_models import ProjectAgentTaskSubmit
|
|
6
6
|
from .task_context_temporary_owner import temporary_task_owner
|
|
7
7
|
from .project_agent_delegations import StoreAgentDelegations
|
|
8
|
+
from .project_agent_remote_origins import StoreRemoteAgentOrigins
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
class StoreProjectAgentContext(StoreAgentDelegations):
|
|
11
|
+
class StoreProjectAgentContext(StoreAgentDelegations, StoreRemoteAgentOrigins):
|
|
11
12
|
async def resolve_project_agent_context(self, actor, request):
|
|
12
13
|
self._require_personal()
|
|
13
14
|
space = await self.authorize(actor, request.personal_space_id, 'reader')
|
|
@@ -68,7 +69,7 @@ class StoreProjectAgentContext(StoreAgentDelegations):
|
|
|
68
69
|
)
|
|
69
70
|
if selected and not self._implicit_owner_allowed(selected[0], fallback_selection):
|
|
70
71
|
selected, reason, basis = [], 'no_match', [
|
|
71
|
-
'management or
|
|
72
|
+
'management or AR peer cannot own unrelated project context implicitly',
|
|
72
73
|
]
|
|
73
74
|
if selected:
|
|
74
75
|
reason = 'project_context_fallback'
|
|
@@ -113,7 +113,7 @@ class StoreProjectAgentCoordinationPolicy:
|
|
|
113
113
|
if (profile.status != 'active' or not active_assignment
|
|
114
114
|
or profile.agent_type != 'durable'
|
|
115
115
|
or any(cap.startswith('fuli.employee:') for cap in profile.capabilities)):
|
|
116
|
-
raise HTTPException(status_code=422, detail='team roles require active durable project assignments;
|
|
116
|
+
raise HTTPException(status_code=422, detail='team roles require active durable project assignments; AR and project managers remain peers')
|
|
117
117
|
updated_at = now_utc()
|
|
118
118
|
written, _, _ = await self.runtime.driver.execute_query(
|
|
119
119
|
'''
|
|
@@ -41,6 +41,19 @@ class StoreProjectAgentTaskActivity:
|
|
|
41
41
|
space,
|
|
42
42
|
request.personal_project_id,
|
|
43
43
|
)
|
|
44
|
+
if request.judgment_task_context_token:
|
|
45
|
+
# Match the task-context lifecycle lock order: session, then task.
|
|
46
|
+
# A new host turn cannot supersede the checked token before commit.
|
|
47
|
+
contexts, _, _ = await self.runtime.driver.execute_query(
|
|
48
|
+
'''
|
|
49
|
+
MATCH (:FuliSpace {id: $space_id, kind: 'personal'})-
|
|
50
|
+
[:HAS_TASK_CONTEXT_SESSION]->(session:FuliTaskContextSession)-
|
|
51
|
+
[:HAS_CONTEXT]->(context:FuliTaskContext {token: $token})
|
|
52
|
+
SET session.write_serial = coalesce(session.write_serial, 0) + 1
|
|
53
|
+
RETURN context.token AS token
|
|
54
|
+
''', space_id=request.personal_space_id, token=request.judgment_task_context_token)
|
|
55
|
+
if not contexts:
|
|
56
|
+
raise HTTPException(403, 'Judgment acceptance context is unavailable')
|
|
44
57
|
# Acquire the task's write lock before reading its revision, transition
|
|
45
58
|
# state or idempotency events. A transaction alone permits two concurrent
|
|
46
59
|
# readers to validate the same revision before either obtains a lock.
|
|
@@ -96,6 +109,19 @@ class StoreProjectAgentTaskActivity:
|
|
|
96
109
|
if (not request.artifact_revision or raw_task.get('quality_gate') != 'passed'
|
|
97
110
|
or raw_task.get('verified_artifact_revision') != request.artifact_revision):
|
|
98
111
|
raise HTTPException(409, 'Verify this exact artifact revision before completing the task')
|
|
112
|
+
if request.expected_verification_attempt_id is not None:
|
|
113
|
+
if (request.status != 'completed' or raw_task.get('quality_gate') != 'passed'
|
|
114
|
+
or raw_task.get('verified_attempt_id') != request.expected_verification_attempt_id
|
|
115
|
+
or raw_task.get('verified_artifact_revision') != request.artifact_revision):
|
|
116
|
+
raise HTTPException(409, 'Verification evidence changed before task acceptance')
|
|
117
|
+
if not request.judgment_task_context_token or not request.source_application:
|
|
118
|
+
raise HTTPException(403, 'Judgment acceptance requires a current lead context')
|
|
119
|
+
context = await self.get_task_context(actor, request.personal_space_id,
|
|
120
|
+
request.judgment_task_context_token, request.source_application)
|
|
121
|
+
if (context.get('personal_project_id') != request.personal_project_id
|
|
122
|
+
or context.get('project_agent_id') != raw_task.get('lead_agent_id')
|
|
123
|
+
or request.agent_id != raw_task.get('lead_agent_id')):
|
|
124
|
+
raise HTTPException(403, 'Judgment acceptance requires the current accountable lead')
|
|
99
125
|
self._validate_task_transition(
|
|
100
126
|
raw_task.get('status'),
|
|
101
127
|
request.status,
|
|
@@ -618,7 +618,7 @@ class StoreProjectAgentTaskReads:
|
|
|
618
618
|
if must_disclose:
|
|
619
619
|
notice = ' '.join(
|
|
620
620
|
(
|
|
621
|
-
f'
|
|
621
|
+
f'AR {item.hr_agent_id} recruited '
|
|
622
622
|
f'{item.proposed_profile.name} as a '
|
|
623
623
|
f'{item.position_kind} {item.participant_role} Agent for '
|
|
624
624
|
f'{item.proposed_profile.responsibility}. '
|
|
@@ -33,7 +33,7 @@ RECRUITMENT_CLAIM_TTL = timedelta(minutes=2)
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
class StoreProjectAgentTaskRecruitment:
|
|
36
|
-
"""Own the audited
|
|
36
|
+
"""Own the audited AR lifecycle independently from task routing mechanics."""
|
|
37
37
|
|
|
38
38
|
async def get_project_agent_recruitment_policy(
|
|
39
39
|
self,
|
|
@@ -202,7 +202,7 @@ class StoreProjectAgentTaskRecruitment:
|
|
|
202
202
|
if not raw.get('hr_agent_id'):
|
|
203
203
|
raise HTTPException(
|
|
204
204
|
status_code=409,
|
|
205
|
-
detail='recruitment cannot proceed without an active
|
|
205
|
+
detail='recruitment cannot proceed without an active AR Agent',
|
|
206
206
|
)
|
|
207
207
|
raw['_guarded_task_id'] = raw['task_id']
|
|
208
208
|
selected = await self._provision_recruitment(actor, raw)
|
|
@@ -321,7 +321,7 @@ class StoreProjectAgentTaskRecruitment:
|
|
|
321
321
|
model_strategy_source=model_source,
|
|
322
322
|
outcome=outcome,
|
|
323
323
|
reason=recruitment['reason_code'],
|
|
324
|
-
match_basis=['
|
|
324
|
+
match_basis=['AR recruitment approved and Agent assigned'],
|
|
325
325
|
candidate_agent_ids=[routing_selected['agent_id']],
|
|
326
326
|
created_at=now,
|
|
327
327
|
**self._decision_executor_fields(executor_decision),
|
|
@@ -333,7 +333,7 @@ class StoreProjectAgentTaskRecruitment:
|
|
|
333
333
|
)
|
|
334
334
|
explanation = self._routing_explanation(
|
|
335
335
|
recruitment['reason_code'],
|
|
336
|
-
['
|
|
336
|
+
['AR recruitment approved and Agent assigned'],
|
|
337
337
|
executor_decision,
|
|
338
338
|
)
|
|
339
339
|
event_id = stable_uuid(
|
|
@@ -445,7 +445,7 @@ class StoreProjectAgentTaskRecruitment:
|
|
|
445
445
|
decision_id=decision_id,
|
|
446
446
|
decision_json=decision.model_dump_json(),
|
|
447
447
|
event_id=event_id,
|
|
448
|
-
event_summary=f'
|
|
448
|
+
event_summary=f'AR recruitment approved: {approval_reason}',
|
|
449
449
|
expected_task_revision=expected_task_revision,
|
|
450
450
|
updated_at=now,
|
|
451
451
|
)
|
|
@@ -13,10 +13,11 @@ from .system_hr_identity import LEGACY_HR_AGENT_ID
|
|
|
13
13
|
|
|
14
14
|
IMPLICIT_PEER_AGENT_IDS = frozenset({
|
|
15
15
|
'employee.jefa',
|
|
16
|
+
'employee.tonborg',
|
|
16
17
|
SYSTEM_HR_AGENT_ID.casefold(),
|
|
17
18
|
LEGACY_HR_AGENT_ID.casefold(),
|
|
18
19
|
})
|
|
19
|
-
IMPLICIT_PEER_CAPABILITIES = frozenset({'fuli.employee:jefa', 'fuli.employee:bole'})
|
|
20
|
+
IMPLICIT_PEER_CAPABILITIES = frozenset({'fuli.employee:jefa', 'fuli.employee:bole', 'fuli.employee:tonborg'})
|
|
20
21
|
IMPLICIT_PEER_AGENT_TYPES = frozenset({'hr'})
|
|
21
22
|
|
|
22
23
|
|
|
@@ -83,7 +84,7 @@ class StoreProjectAgentTaskStaffing:
|
|
|
83
84
|
return [lead], [lead, *members], 'project_team_lead', [
|
|
84
85
|
'project team lead is the stable conversation owner',
|
|
85
86
|
f'{len(members)} active team member(s) available for explicit workstreams',
|
|
86
|
-
'
|
|
87
|
+
'AR and project manager remain peer roles',
|
|
87
88
|
]
|
|
88
89
|
normalized_work_kind = request.work_kind.casefold()
|
|
89
90
|
required = {item.casefold() for item in request.required_capabilities}
|
|
@@ -885,7 +886,7 @@ class StoreProjectAgentTaskStaffing:
|
|
|
885
886
|
status_code=422,
|
|
886
887
|
detail=(
|
|
887
888
|
'parallel work requires at least two active Agents; '
|
|
888
|
-
'automatic recruitment needs an active
|
|
889
|
+
'automatic recruitment needs an active AR Agent, otherwise '
|
|
889
890
|
'recruit another qualified collaborator or assign enough '
|
|
890
891
|
'existing Agents before enabling parallel work'
|
|
891
892
|
),
|
|
@@ -272,7 +272,7 @@ class StoreProjectAgentTasks(
|
|
|
272
272
|
*match_basis,
|
|
273
273
|
'parallel automatic recruitment: filled '
|
|
274
274
|
f'{recruited_count} missing participant slot(s)',
|
|
275
|
-
'
|
|
275
|
+
'AR recruitment recorded before Agent creation',
|
|
276
276
|
]
|
|
277
277
|
if recruited_lead:
|
|
278
278
|
model_strategy, model_source = self._effective_model_strategy(
|
|
@@ -322,7 +322,7 @@ class StoreProjectAgentTasks(
|
|
|
322
322
|
else 'no_match'
|
|
323
323
|
)
|
|
324
324
|
match_basis = [
|
|
325
|
-
'
|
|
325
|
+
'AR recruitment recorded before Agent creation',
|
|
326
326
|
*(
|
|
327
327
|
[
|
|
328
328
|
'parallel auto-staffing: added '
|
|
@@ -68,11 +68,11 @@ class StoreProjectAgents:
|
|
|
68
68
|
) -> ProjectAgentRecord:
|
|
69
69
|
self._require_personal()
|
|
70
70
|
if request.agent_id == LEGACY_HR_AGENT_ID:
|
|
71
|
-
raise HTTPException(409, 'This
|
|
71
|
+
raise HTTPException(409, 'This AR identity has moved to employee.bole')
|
|
72
72
|
if request.profile.agent_type == 'temporary' and not recruitment_id:
|
|
73
73
|
raise HTTPException(
|
|
74
74
|
status_code=422,
|
|
75
|
-
detail='temporary Agents must be created by an audited
|
|
75
|
+
detail='temporary Agents must be created by an audited AR recruitment',
|
|
76
76
|
)
|
|
77
77
|
if (
|
|
78
78
|
request.profile.agent_type == 'coordinator'
|
|
@@ -88,7 +88,7 @@ class StoreProjectAgents:
|
|
|
88
88
|
):
|
|
89
89
|
raise HTTPException(
|
|
90
90
|
status_code=422,
|
|
91
|
-
detail='only the system-managed Bole identity may use
|
|
91
|
+
detail='only the system-managed Bole identity may use AR type',
|
|
92
92
|
)
|
|
93
93
|
if (
|
|
94
94
|
request.agent_id == SYSTEM_COORDINATOR_AGENT_ID
|
|
@@ -104,7 +104,7 @@ class StoreProjectAgents:
|
|
|
104
104
|
):
|
|
105
105
|
raise HTTPException(
|
|
106
106
|
status_code=422,
|
|
107
|
-
detail='the system
|
|
107
|
+
detail='the system AR identity cannot change Agent type',
|
|
108
108
|
)
|
|
109
109
|
if (
|
|
110
110
|
request.agent_id in (SYSTEM_HR_AGENT_ID, SYSTEM_COORDINATOR_AGENT_ID)
|
|
@@ -112,7 +112,7 @@ class StoreProjectAgents:
|
|
|
112
112
|
):
|
|
113
113
|
raise HTTPException(
|
|
114
114
|
status_code=422,
|
|
115
|
-
detail='the project manager and
|
|
115
|
+
detail='the project manager and AR must remain active',
|
|
116
116
|
)
|
|
117
117
|
space = await self.authorize(actor, request.personal_space_id, 'maintainer')
|
|
118
118
|
if request.personal_project_id:
|
|
@@ -402,7 +402,7 @@ class StoreProjectAgents:
|
|
|
402
402
|
if agent_id == SYSTEM_HR_AGENT_ID:
|
|
403
403
|
raise HTTPException(
|
|
404
404
|
status_code=422,
|
|
405
|
-
detail='the system
|
|
405
|
+
detail='the system AR cannot be archived',
|
|
406
406
|
)
|
|
407
407
|
await self.authorize(actor, personal_space_id, 'maintainer')
|
|
408
408
|
if not reason.strip():
|