fuli-context 0.7.5 → 0.7.7
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 +69 -13
- package/README.zh-CN.md +60 -14
- package/dist/web/assets/{AboutPage-ByCUor-j.js → AboutPage-BcZeFcHe.js} +1 -1
- package/dist/web/assets/{ConnectionsPage-DkdkZ40Q.js → ConnectionsPage-xAQYl1SQ.js} +1 -1
- package/dist/web/assets/EmployeeRecruitDialog-CbPAmhy7.js +1 -0
- package/dist/web/assets/EmployeeRecruitDialog-CfFlyKSh.css +1 -0
- package/dist/web/assets/EmployeeWorkbenchPage-BQvTLoc8.css +1 -0
- package/dist/web/assets/EmployeeWorkbenchPage-DF3EL0Ui.js +1 -0
- package/dist/web/assets/KnowledgeEditDialog-UnnBDt0I.js +1 -0
- package/dist/web/assets/{KnowledgeEditDialog-DWUV_NPp.css → KnowledgeEditDialog-iv-dgUeD.css} +1 -1
- package/dist/web/assets/{KnowledgeInspector-CYAjAb9J.js → KnowledgeInspector-BqPJH5Rr.js} +1 -1
- package/dist/web/assets/{KnowledgeOrganizerPage-CV4F54R_.js → KnowledgeOrganizerPage-CyvRflXM.js} +1 -1
- package/dist/web/assets/KnowledgePage-59eczTqe.js +1 -0
- package/dist/web/assets/{KnowledgeWorkspace-z4u5mXOG.js → KnowledgeWorkspace-CfIjhOog.js} +1 -1
- package/dist/web/assets/{OverviewPage-BWOfeS6k.js → OverviewPage-Xyfm5xkZ.js} +1 -1
- package/dist/web/assets/{PersonalProfilePage-c3w4-h7a.js → PersonalProfilePage-DB9mByB3.js} +1 -1
- package/dist/web/assets/PersonalProjectsPage-_-YXVi5O.js +1 -0
- package/dist/web/assets/ProjectAgentsPage-C3vW6wmk.css +1 -0
- package/dist/web/assets/ProjectAgentsPage-DeeQByPX.js +11 -0
- package/dist/web/assets/{PublicProjectsPage-DdH0oGB3.js → PublicProjectsPage-BEZ1YPs3.js} +1 -1
- package/dist/web/assets/{ReviewPage-BYIpcIHG.js → ReviewPage-CQYGwFBH.js} +1 -1
- package/dist/web/assets/SearchableSelect-D27H0MoP.js +1 -0
- package/dist/web/assets/{SettingsPage-2VkbXgZi.js → SettingsPage-BlEZ03Yi.js} +1 -1
- package/dist/web/assets/{WritingTastePage-BOPQZuva.js → WritingTastePage-DvRt_nmP.js} +1 -1
- package/dist/web/assets/catalog-DFvIaHVS.js +1 -0
- package/dist/web/assets/{console-BlAhujsH.js → console-tOxOl8Vq.js} +1 -1
- package/dist/web/assets/conversation-launcher-settings-DuFSpPAo.js +1 -0
- package/dist/web/assets/{i18n-Ci3heuiL.js → i18n-CgBHZthI.js} +3 -3
- package/dist/web/assets/{identity-4_-v6cB7.js → identity-CFo_Uw1H.js} +1 -1
- package/dist/web/assets/index-CJc5ppXH.css +1 -0
- package/dist/web/assets/index-yCi3cz2l.js +2 -0
- package/dist/web/assets/{model-yG0YqUyZ.js → model-BtnVr7fX.js} +1 -1
- package/dist/web/assets/{useApi-CROJJdhE-twhdZArA.js → useApi-CROJJdhE-B6MHm_Av.js} +1 -1
- package/dist/web/assets/{vue-router-C-wot_Di.js → vue-router-CzHm-y-x.js} +1 -1
- package/dist/web/index.html +7 -6
- package/docs/employee-agents.md +99 -0
- package/docs/project-agent-memory.md +479 -0
- package/graph-provider/fuli_graph/knowledge_audit_models.py +171 -0
- package/graph-provider/fuli_graph/model_base.py +5 -0
- package/graph-provider/fuli_graph/model_validation.py +32 -0
- package/graph-provider/fuli_graph/models.py +26 -170
- package/graph-provider/fuli_graph/project_agent_context_models.py +29 -0
- package/graph-provider/fuli_graph/project_agent_coordination_models.py +22 -0
- package/graph-provider/fuli_graph/project_agent_executor_models.py +35 -7
- package/graph-provider/fuli_graph/project_agent_memory_models.py +79 -0
- package/graph-provider/fuli_graph/project_agent_memory_routes.py +46 -0
- package/graph-provider/fuli_graph/project_agent_models.py +9 -0
- package/graph-provider/fuli_graph/project_agent_recruitment_values.py +48 -0
- package/graph-provider/fuli_graph/project_agent_routes.py +50 -1
- package/graph-provider/fuli_graph/project_agent_task_models.py +220 -29
- package/graph-provider/fuli_graph/runtime.py +37 -1
- package/graph-provider/fuli_graph/store.py +6 -0
- package/graph-provider/fuli_graph/store_project_agent_context.py +65 -0
- package/graph-provider/fuli_graph/store_project_agent_coordination_policy.py +122 -0
- package/graph-provider/fuli_graph/store_project_agent_executor_learning.py +175 -11
- package/graph-provider/fuli_graph/store_project_agent_executor_routing.py +33 -10
- package/graph-provider/fuli_graph/store_project_agent_executors.py +181 -14
- package/graph-provider/fuli_graph/store_project_agent_memory.py +174 -0
- package/graph-provider/fuli_graph/store_project_agent_task_activity.py +423 -0
- package/graph-provider/fuli_graph/store_project_agent_task_persistence.py +152 -0
- package/graph-provider/fuli_graph/store_project_agent_task_reads.py +187 -21
- package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +756 -164
- package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +396 -7
- package/graph-provider/fuli_graph/store_project_agent_tasks.py +265 -465
- package/graph-provider/fuli_graph/store_project_agents.py +214 -46
- package/graph-provider/fuli_graph/store_task_contexts.py +217 -0
- package/graph-provider/fuli_graph/store_transactions.py +40 -0
- package/graph-provider/fuli_graph/task_context_models.py +50 -0
- package/graph-provider/fuli_graph/task_context_routes.py +31 -0
- package/graph-provider/fuli_graph/workflow_observation_models.py +1 -1
- package/graph-provider/pyproject.toml +1 -1
- package/npm-shrinkwrap.json +8 -8
- package/package.json +5 -2
- package/skills/capturing-session-knowledge/SKILL.md +18 -6
- package/src/adaptive-runtime/executor-pool.js +46 -5
- package/src/adaptive-runtime/graph-services.js +10 -3
- package/src/adaptive-runtime/lease-client.js +66 -14
- package/src/adaptive-runtime/runtime-broker.js +24 -9
- package/src/agent-tools/employee-definitions.js +43 -0
- package/src/agent-tools/graph-definitions.js +48 -6
- package/src/agent-tools/graph-handlers.js +15 -0
- package/src/agent-tools/project-agent-definitions.js +35 -4
- package/src/agent-tools/project-agent-memory-definitions.js +36 -0
- package/src/agent-tools.js +6 -2
- package/src/agents/codex/lifecycle-hook.js +107 -0
- package/src/agents/codex/lifecycle-hooks.js +231 -0
- package/src/agents/cursor/lifecycle-hook.js +133 -0
- package/src/agents/cursor/lifecycle-hooks.js +50 -0
- package/src/agents/lifecycle-message.js +19 -0
- package/src/app/agent-request-context.js +23 -0
- package/src/cli/command-registry.js +2 -0
- package/src/cli/employee-command.js +17 -0
- package/src/cli/remote-mcp-command.js +282 -0
- package/src/cli/setup-command.js +5 -0
- package/src/cli.js +10 -0
- package/src/employees/catalog/jefa.json +21 -0
- package/src/employees/install-package.js +77 -0
- package/src/employees/management-policy.js +88 -0
- package/src/employees/manifest.js +53 -0
- package/src/employees/package-registry.js +107 -0
- package/src/employees/project-selection.js +50 -0
- package/src/employees/recruitment.js +199 -0
- package/src/employees/service.js +83 -0
- package/src/employees/task-entry.js +39 -0
- package/src/graphiti/agent-knowledge-workflows.js +135 -25
- package/src/graphiti/federated-application.js +78 -35
- package/src/graphiti/project-agent-control-plane.js +32 -3
- package/src/graphiti/project-agent-mapping.js +175 -4
- package/src/graphiti/project-agent-memory.js +115 -0
- package/src/graphiti/project-agent-task-entry.js +126 -0
- package/src/graphiti/project-agent-workflows.js +49 -14
- package/src/graphiti/provider-client.js +78 -4
- package/src/http/api-router.js +25 -5
- package/src/http/employee-api-router.js +64 -0
- package/src/http/error-mapping.js +4 -0
- package/src/http/graph-api-router.js +14 -0
- package/src/http/request-policy.js +6 -3
- package/src/http/static-handler.js +4 -3
- package/src/mcp/create-mcp-server.js +98 -37
- package/src/mcp/direct-call.js +38 -0
- package/src/mcp/instructions.js +8 -7
- package/src/mcp/provider-task-context-registry.js +74 -0
- package/src/mcp/remote-http-server.js +761 -0
- package/src/mcp/session-id.js +29 -3
- package/src/mcp/task-context-registry.js +88 -29
- package/src/mcp/tool-annotations.js +12 -2
- package/src/mcp/tool-result.js +8 -3
- package/src/mcp-server.js +20 -8
- package/src/native-runtime/runtime.js +58 -8
- package/src/server.js +76 -28
- package/src/setup/agent-installation-status.js +38 -6
- package/src/setup/agents.js +20 -8
- package/src/setup/claude-code-config.js +55 -13
- package/src/setup/codex-bootstrap.js +11 -10
- package/src/setup/codex-config.js +74 -7
- package/src/setup/config-backup.js +18 -2
- package/src/setup/cursor-config.js +22 -5
- package/src/setup/setup.js +30 -22
- package/src/storage/json-file.js +11 -3
- package/dist/web/assets/KnowledgeEditDialog-DSe3Gwep.js +0 -1
- package/dist/web/assets/KnowledgePage-vYYXipOi.js +0 -1
- package/dist/web/assets/PersonalProjectsPage-B7AlLEwf.js +0 -1
- package/dist/web/assets/ProjectAgentsPage-CgftpIPB.css +0 -1
- package/dist/web/assets/ProjectAgentsPage-DTHcL_ox.js +0 -11
- package/dist/web/assets/SearchableSelect-Dz13ClQb.js +0 -1
- package/dist/web/assets/conversation-launcher-settings-DTYxiLJt.js +0 -1
- package/dist/web/assets/index-BnH5mcZx.css +0 -1
- package/dist/web/assets/index-C67Q0JY2.js +0 -2
|
@@ -7,27 +7,142 @@ import json
|
|
|
7
7
|
|
|
8
8
|
from .project_agent_models import ProjectAgentModelStrategy, ProjectAgentProfile
|
|
9
9
|
from .project_agent_task_models import (
|
|
10
|
+
ProjectAgentActivityTask,
|
|
10
11
|
ProjectAgentRoutingDecisionRecord,
|
|
11
12
|
ProjectAgentTaskExecutionSummary,
|
|
12
13
|
ProjectAgentTaskEventRecord,
|
|
13
14
|
ProjectAgentTaskParticipantRecord,
|
|
14
15
|
ProjectAgentTaskRecord,
|
|
15
16
|
ProjectAgentTaskRouteResult,
|
|
17
|
+
ProjectAgentTokenUsage,
|
|
18
|
+
ProjectAgentWorkerRuntime,
|
|
16
19
|
)
|
|
17
20
|
from .provider_values import native_datetime
|
|
18
21
|
|
|
19
22
|
|
|
20
23
|
TERMINAL_TASK_STATUSES = {'completed', 'failed', 'cancelled'}
|
|
24
|
+
SOURCE_SESSION_FIELDS = {
|
|
25
|
+
'source_application', 'source_session_id', 'source_session_url',
|
|
26
|
+
}
|
|
27
|
+
WORKER_DISPLAY_FIELDS = {'worker_label', 'worker_occupation_emoji'}
|
|
21
28
|
|
|
22
29
|
|
|
23
30
|
class StoreProjectAgentTaskReads:
|
|
24
31
|
"""Read durable task history and derive client-facing route results."""
|
|
25
32
|
|
|
26
|
-
|
|
33
|
+
@classmethod
|
|
34
|
+
def _event_with_latest_execution_evidence(cls, event, event_rows):
|
|
35
|
+
reported = sorted(
|
|
36
|
+
(dict(item) for item in event_rows),
|
|
37
|
+
key=lambda item: (
|
|
38
|
+
str(item.get('created_at') or ''),
|
|
39
|
+
str(item.get('event_id') or ''),
|
|
40
|
+
),
|
|
41
|
+
)
|
|
42
|
+
merged = dict(event)
|
|
43
|
+
worker_id = event.get('worker_id') or next(
|
|
44
|
+
(item['worker_id'] for item in reversed(reported)
|
|
45
|
+
if item.get('worker_id')), None,
|
|
46
|
+
)
|
|
47
|
+
if worker_id:
|
|
48
|
+
reported = [item for item in reported
|
|
49
|
+
if item.get('worker_id') == worker_id]
|
|
50
|
+
worker_events = reported
|
|
51
|
+
reported = cls._current_worker_run(reported)
|
|
52
|
+
source_events = cls._current_reporter_session(reported)
|
|
53
|
+
for field in (
|
|
54
|
+
'source_application', 'source_session_id', 'source_session_url',
|
|
55
|
+
'tools_used', 'actual_model_provider', 'actual_model',
|
|
56
|
+
'actual_executor_id', 'matched_executor_rule_id',
|
|
57
|
+
'token_usage_json', 'worker_id', 'worker_label',
|
|
58
|
+
'worker_occupation_emoji', 'worker_status', 'worker_runtime_json',
|
|
59
|
+
):
|
|
60
|
+
scope = worker_events if field in WORKER_DISPLAY_FIELDS else (
|
|
61
|
+
source_events if field in SOURCE_SESSION_FIELDS else reported
|
|
62
|
+
)
|
|
63
|
+
if reported and (worker_id or field in SOURCE_SESSION_FIELDS):
|
|
64
|
+
merged[field] = None
|
|
65
|
+
for candidate in reversed(scope):
|
|
66
|
+
value = candidate.get(field)
|
|
67
|
+
if value is not None and value != '':
|
|
68
|
+
merged[field] = value
|
|
69
|
+
break
|
|
70
|
+
return merged
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def _event_json_fields(event):
|
|
74
|
+
fields = {}
|
|
75
|
+
for name, model in (
|
|
76
|
+
('token_usage', ProjectAgentTokenUsage),
|
|
77
|
+
('worker_runtime', ProjectAgentWorkerRuntime),
|
|
78
|
+
):
|
|
79
|
+
value = event.get(f'{name}_json')
|
|
80
|
+
if value:
|
|
81
|
+
try:
|
|
82
|
+
fields[name] = model.model_validate_json(value)
|
|
83
|
+
except (TypeError, ValueError):
|
|
84
|
+
pass
|
|
85
|
+
return fields
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def _current_worker_run(cls, events):
|
|
89
|
+
"""Never carry usage, tools, or model claims across worker sessions."""
|
|
90
|
+
start, identity = 0, None
|
|
91
|
+
for index, event in enumerate(events):
|
|
92
|
+
runtime = cls._event_json_fields(event).get('worker_runtime') \
|
|
93
|
+
if isinstance(event, dict) else event.worker_runtime
|
|
94
|
+
if runtime is None:
|
|
95
|
+
continue
|
|
96
|
+
reported_identity = (runtime.application, runtime.session_id)
|
|
97
|
+
if reported_identity != identity:
|
|
98
|
+
start, identity = index, reported_identity
|
|
99
|
+
return events[start:]
|
|
100
|
+
|
|
101
|
+
@staticmethod
|
|
102
|
+
def _current_reporter_session(events):
|
|
103
|
+
"""Keep a reporting session URL paired with its own host identity."""
|
|
104
|
+
start, identity = 0, None
|
|
105
|
+
for index, event in enumerate(events):
|
|
106
|
+
get = event.get if isinstance(event, dict) else (
|
|
107
|
+
lambda field: getattr(event, field, None)
|
|
108
|
+
)
|
|
109
|
+
reported = (get('source_application'), get('source_session_id'))
|
|
110
|
+
if reported == (None, None):
|
|
111
|
+
continue
|
|
112
|
+
if reported != identity:
|
|
113
|
+
start, identity = index, reported
|
|
114
|
+
return events[start:]
|
|
115
|
+
|
|
116
|
+
@classmethod
|
|
117
|
+
def _activity_executor_fields(cls, event):
|
|
118
|
+
fields = {
|
|
119
|
+
name: event.get(name)
|
|
120
|
+
for name in (
|
|
121
|
+
'actual_executor_id', 'matched_executor_rule_id',
|
|
122
|
+
'source_session_id', 'source_session_url', 'tools_used',
|
|
123
|
+
'worker_id', 'worker_label', 'worker_occupation_emoji',
|
|
124
|
+
'worker_status',
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
fields.update(cls._event_json_fields(event))
|
|
128
|
+
return {key: value for key, value in fields.items()
|
|
129
|
+
if key in ProjectAgentActivityTask.model_fields}
|
|
130
|
+
|
|
131
|
+
async def _find_task_row(
|
|
132
|
+
self,
|
|
133
|
+
personal_space_id,
|
|
134
|
+
task_id,
|
|
135
|
+
personal_project_id=None,
|
|
136
|
+
):
|
|
27
137
|
records, _, _ = await self.runtime.driver.execute_query(
|
|
28
|
-
self._task_read_query(
|
|
138
|
+
self._task_read_query(
|
|
139
|
+
'''WHERE task.task_id = $task_id
|
|
140
|
+
AND ($personal_project_id IS NULL
|
|
141
|
+
OR task.personal_project_id = $personal_project_id)'''
|
|
142
|
+
),
|
|
29
143
|
personal_space_id=personal_space_id,
|
|
30
144
|
task_id=task_id,
|
|
145
|
+
personal_project_id=personal_project_id,
|
|
31
146
|
routing_='r',
|
|
32
147
|
)
|
|
33
148
|
return records[0] if records else None
|
|
@@ -119,9 +234,12 @@ class StoreProjectAgentTaskReads:
|
|
|
119
234
|
'worker_label',
|
|
120
235
|
'worker_occupation_emoji',
|
|
121
236
|
'worker_status',
|
|
237
|
+
'source_session_url',
|
|
238
|
+
'tools_used',
|
|
122
239
|
):
|
|
123
240
|
if name in ProjectAgentTaskEventRecord.model_fields:
|
|
124
241
|
fields[name] = event.get(name)
|
|
242
|
+
fields.update(self._event_json_fields(event))
|
|
125
243
|
events.append(ProjectAgentTaskEventRecord(**fields))
|
|
126
244
|
events.sort(key=lambda item: item.created_at)
|
|
127
245
|
fields = {
|
|
@@ -132,6 +250,9 @@ class StoreProjectAgentTaskReads:
|
|
|
132
250
|
'objective': raw['objective'],
|
|
133
251
|
'work_kind': raw['work_kind'],
|
|
134
252
|
'required_capabilities': list(raw.get('required_capabilities') or []),
|
|
253
|
+
'executor_capability_hints': list(
|
|
254
|
+
raw.get('executor_capability_hints') or []
|
|
255
|
+
),
|
|
135
256
|
'duration': raw.get('duration') or 'ongoing',
|
|
136
257
|
'staffing_intent': raw.get('staffing_intent') or 'reuse_preferred',
|
|
137
258
|
'status': raw['status'],
|
|
@@ -298,14 +419,16 @@ class StoreProjectAgentTaskReads:
|
|
|
298
419
|
or event.actual_executor_id
|
|
299
420
|
or event.actual_model_provider
|
|
300
421
|
or event.actual_model
|
|
422
|
+
or event.token_usage
|
|
301
423
|
or event.status in {
|
|
302
424
|
'running', 'paused', 'awaiting_review', 'blocked',
|
|
303
425
|
'completed', 'failed', 'cancelled',
|
|
304
426
|
}
|
|
305
427
|
)
|
|
306
428
|
|
|
307
|
-
@
|
|
429
|
+
@classmethod
|
|
308
430
|
def _execution_summary_row(
|
|
431
|
+
cls,
|
|
309
432
|
participant,
|
|
310
433
|
agent_name,
|
|
311
434
|
occupation_emoji,
|
|
@@ -316,12 +439,17 @@ class StoreProjectAgentTaskReads:
|
|
|
316
439
|
):
|
|
317
440
|
task_fallback = task_fallback or {}
|
|
318
441
|
latest = events[-1] if events else None
|
|
442
|
+
run_events = cls._current_worker_run(events)
|
|
443
|
+
source_events = cls._current_reporter_session(run_events)
|
|
319
444
|
|
|
320
445
|
def latest_value(name):
|
|
446
|
+
scope = events if name in WORKER_DISPLAY_FIELDS else (
|
|
447
|
+
source_events if name in SOURCE_SESSION_FIELDS else run_events
|
|
448
|
+
)
|
|
321
449
|
return next(
|
|
322
450
|
(
|
|
323
451
|
getattr(event, name)
|
|
324
|
-
for event in reversed(
|
|
452
|
+
for event in reversed(scope)
|
|
325
453
|
if getattr(event, name, None) is not None
|
|
326
454
|
),
|
|
327
455
|
None,
|
|
@@ -347,6 +475,9 @@ class StoreProjectAgentTaskReads:
|
|
|
347
475
|
executor=actual_executor_id,
|
|
348
476
|
executor_id=actual_executor_id,
|
|
349
477
|
source_application=latest_value('source_application'),
|
|
478
|
+
source_session_id=latest_value('source_session_id'),
|
|
479
|
+
source_session_url=latest_value('source_session_url'),
|
|
480
|
+
tools_used=latest_value('tools_used'),
|
|
350
481
|
actual_model_provider=(
|
|
351
482
|
latest_value('actual_model_provider')
|
|
352
483
|
or task_fallback.get('actual_model_provider')
|
|
@@ -355,6 +486,7 @@ class StoreProjectAgentTaskReads:
|
|
|
355
486
|
latest_value('actual_model')
|
|
356
487
|
or task_fallback.get('actual_model')
|
|
357
488
|
),
|
|
489
|
+
token_usage=latest_value('token_usage'),
|
|
358
490
|
work_summary=(
|
|
359
491
|
latest.summary
|
|
360
492
|
if latest
|
|
@@ -365,6 +497,7 @@ class StoreProjectAgentTaskReads:
|
|
|
365
497
|
worker_id=worker_id,
|
|
366
498
|
worker_label=worker_label,
|
|
367
499
|
worker_occupation_emoji=worker_occupation_emoji,
|
|
500
|
+
worker_runtime=latest_value('worker_runtime'),
|
|
368
501
|
)
|
|
369
502
|
|
|
370
503
|
async def _archive_finished_temporary_agent(self, request, raw_task, updated_at):
|
|
@@ -381,7 +514,14 @@ class StoreProjectAgentTaskReads:
|
|
|
381
514
|
agent_type: 'temporary',
|
|
382
515
|
temporary_task_id: $task_id
|
|
383
516
|
})
|
|
517
|
+
SET agent._task_lifecycle_lock = true
|
|
518
|
+
REMOVE agent._task_lifecycle_lock
|
|
519
|
+
WITH space, task, agent
|
|
384
520
|
SET agent.status = 'archived',
|
|
521
|
+
agent.archive_reason = coalesce(
|
|
522
|
+
agent.archive_reason,
|
|
523
|
+
'temporary task reached terminal state'),
|
|
524
|
+
agent.archived_at = coalesce(agent.archived_at, $updated_at),
|
|
385
525
|
agent.updated_at = $updated_at
|
|
386
526
|
WITH space, agent
|
|
387
527
|
MATCH (space)-[:CONTAINS_PROJECT]->(:FuliPersonalProject)-
|
|
@@ -392,7 +532,7 @@ class StoreProjectAgentTaskReads:
|
|
|
392
532
|
assignment.end_reason = 'temporary task reached terminal state',
|
|
393
533
|
assignment.ended_at = $updated_at,
|
|
394
534
|
assignment.updated_at = $updated_at,
|
|
395
|
-
assignment.revision = assignment.revision + 1
|
|
535
|
+
assignment.revision = coalesce(assignment.revision, 0) + 1
|
|
396
536
|
''',
|
|
397
537
|
personal_space_id=request.personal_space_id,
|
|
398
538
|
task_id=request.task_id,
|
|
@@ -421,10 +561,31 @@ class StoreProjectAgentTaskReads:
|
|
|
421
561
|
json.dumps(payload, sort_keys=True, separators=(',', ':')).encode()
|
|
422
562
|
).hexdigest()
|
|
423
563
|
|
|
424
|
-
def _route_result(
|
|
425
|
-
|
|
564
|
+
def _route_result(
|
|
565
|
+
self,
|
|
566
|
+
task,
|
|
567
|
+
recruitment,
|
|
568
|
+
*,
|
|
569
|
+
assigned_agent=None,
|
|
570
|
+
recruitments=None,
|
|
571
|
+
):
|
|
572
|
+
all_recruitments = list(recruitments or [])
|
|
573
|
+
if recruitment and not any(
|
|
574
|
+
item.recruitment_id == recruitment.recruitment_id
|
|
575
|
+
for item in all_recruitments
|
|
576
|
+
):
|
|
577
|
+
all_recruitments.insert(0, recruitment)
|
|
578
|
+
fulfilled = [
|
|
579
|
+
item for item in all_recruitments if item.status == 'fulfilled'
|
|
580
|
+
]
|
|
581
|
+
awaiting = [
|
|
582
|
+
item
|
|
583
|
+
for item in all_recruitments
|
|
584
|
+
if item.status == 'awaiting_confirmation'
|
|
585
|
+
]
|
|
586
|
+
if all_recruitments and len(fulfilled) == len(all_recruitments):
|
|
426
587
|
decision = 'recruited'
|
|
427
|
-
elif
|
|
588
|
+
elif awaiting:
|
|
428
589
|
decision = 'awaiting_confirmation'
|
|
429
590
|
elif task.routing_outcome == 'assigned_existing':
|
|
430
591
|
decision = 'reused'
|
|
@@ -433,25 +594,30 @@ class StoreProjectAgentTaskReads:
|
|
|
433
594
|
else:
|
|
434
595
|
decision = 'blocked'
|
|
435
596
|
notice = None
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
597
|
+
disclosed_recruitments = [
|
|
598
|
+
item
|
|
599
|
+
for item in fulfilled
|
|
600
|
+
if item.confirmation_mode == 'automatic'
|
|
601
|
+
]
|
|
602
|
+
must_disclose = bool(disclosed_recruitments)
|
|
441
603
|
if must_disclose:
|
|
442
|
-
notice = (
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
604
|
+
notice = ' '.join(
|
|
605
|
+
(
|
|
606
|
+
f'HR {item.hr_agent_id} recruited '
|
|
607
|
+
f'{item.proposed_profile.name} as a '
|
|
608
|
+
f'{item.position_kind} {item.participant_role} Agent for '
|
|
609
|
+
f'{item.proposed_profile.responsibility}. '
|
|
610
|
+
f'Reason: {item.reason}. '
|
|
611
|
+
f'Trigger: {item.trigger_source_application or "unspecified"}; '
|
|
612
|
+
f'time: {item.fulfilled_at.isoformat() if item.fulfilled_at else "pending"}.'
|
|
613
|
+
)
|
|
614
|
+
for item in disclosed_recruitments
|
|
450
615
|
)
|
|
451
616
|
return ProjectAgentTaskRouteResult(
|
|
452
617
|
task=task,
|
|
453
618
|
assigned_agent=assigned_agent,
|
|
454
619
|
recruitment=recruitment,
|
|
620
|
+
recruitments=all_recruitments,
|
|
455
621
|
decision=decision,
|
|
456
622
|
must_disclose_recruitment=must_disclose,
|
|
457
623
|
client_notice=notice,
|