fuli-context 0.8.1 → 0.8.3
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 +11 -7
- package/README.zh-CN.md +8 -7
- package/dist/web/assets/{AboutPage-fBq5VFoQ.js → AboutPage-HLhChD_Q.js} +1 -1
- package/dist/web/assets/{AgentHand-CMZk5GjY.js → AgentHand-BaLSbn08.js} +1 -1
- package/dist/web/assets/{AgentName-DRZHAIB2.js → AgentName-0PIU7MOX.js} +1 -1
- package/dist/web/assets/{AgentProfilePage-BPQEfMhB.js → AgentProfilePage-9cSsZHnk.js} +1 -1
- package/dist/web/assets/{AgentsDirectoryPage-CKZfrcuD.js → AgentsDirectoryPage-7G02Xm6C.js} +1 -1
- package/dist/web/assets/{ConnectionsPage-COb56qnk.js → ConnectionsPage-D0wPYbwd.js} +1 -1
- package/dist/web/assets/{EmployeeRecruitDialog-BLTpjCcK.js → EmployeeRecruitDialog-BrPH3e50.js} +1 -1
- package/dist/web/assets/{EmployeeWorkbenchPage-HUd-gne2.js → EmployeeWorkbenchPage-u-jw38PW.js} +1 -1
- package/dist/web/assets/{GrowthLoading-Bu_kH-I8.js → GrowthLoading-DFOy-mEl.js} +1 -1
- package/dist/web/assets/{KnowledgeEditDialog-BTwXXRCD.js → KnowledgeEditDialog-DBsjlkDr.js} +1 -1
- package/dist/web/assets/{KnowledgeInspector-Ct5hl2--.js → KnowledgeInspector-C79hbuOo.js} +1 -1
- package/dist/web/assets/{KnowledgeOrganizerPage-DMC8Hh43.js → KnowledgeOrganizerPage-Cz1BnAtz.js} +1 -1
- package/dist/web/assets/KnowledgePage-JyfFFUrg.js +1 -0
- package/dist/web/assets/{KnowledgeWorkspace-DmHIc0jb.js → KnowledgeWorkspace-B2vj12QG.js} +1 -1
- package/dist/web/assets/{NotFoundPage-C0yOR9T9.js → NotFoundPage-D340EQJP.js} +1 -1
- package/dist/web/assets/{OverviewPage-BreX_kb4.js → OverviewPage-BKiYudrC.js} +1 -1
- package/dist/web/assets/{PersonalProfilePage-CX1a6_E3.js → PersonalProfilePage-BIGTcem2.js} +1 -1
- package/dist/web/assets/PersonalProjectsPage-CzjDCIi0.js +1 -0
- package/dist/web/assets/{ProjectAgentsPage-CA04h1Uf.js → ProjectAgentsPage-C5F2WgwJ.js} +4 -4
- package/dist/web/assets/ProjectAgentsPage-C89ZEDgi.css +1 -0
- package/dist/web/assets/{ProjectScopePicker-DVHfXX1f.js → ProjectScopePicker-8hrgvmg-.js} +1 -1
- package/dist/web/assets/{PublicProjectsPage-CtctKml1.js → PublicProjectsPage-b5zx0dFS.js} +1 -1
- package/dist/web/assets/{ReviewPage-DUqaK-do.js → ReviewPage-BLdWYDmj.js} +1 -1
- package/dist/web/assets/{SearchableSelect-BZtyYmZN.js → SearchableSelect-vaoSDq1g.js} +1 -1
- package/dist/web/assets/{SettingsPage-CB4ymLr8.js → SettingsPage-Dxjrkq1g.js} +1 -1
- package/dist/web/assets/{SharedJefaReportPage-CbFtrDCY.js → SharedJefaReportPage-YG2xwQQy.js} +1 -1
- package/dist/web/assets/{WritingTastePage-B7lSzs2z.js → WritingTastePage-CPPduPb1.js} +1 -1
- package/dist/web/assets/{agent-profile.css_vue_type_style_index_0_src_true_lang-C22UdfU0.js → agent-profile.css_vue_type_style_index_0_src_true_lang-LTJ1ptv9.js} +1 -1
- package/dist/web/assets/{catalog-DU4Kfd-N.js → catalog-B9tlJAp9.js} +1 -1
- package/dist/web/assets/{client-CEHkJJ-5.js → client-InXscj8J.js} +1 -1
- package/dist/web/assets/{console--CZxc3PW.js → console-hUns2aD-.js} +1 -1
- package/dist/web/assets/{conversation-launcher-settings-Co1RlTFH.js → conversation-launcher-settings-DEtJ6pZg.js} +1 -1
- package/dist/web/assets/{i18n-BtOOoyJd.js → i18n-75woNt31.js} +2 -2
- package/dist/web/assets/{identity-CsowvtIm.js → identity-Wt2Xio_B.js} +1 -1
- package/dist/web/assets/index-pleRuLpw.js +2 -0
- package/dist/web/assets/{model-CGpllrds.js → model-C1GwBvd9.js} +1 -1
- package/dist/web/assets/{personal-profile-graph-BNTG4PSY.js → personal-profile-graph-BdV_3I1u.js} +1 -1
- package/dist/web/assets/{shared-report-BFUJqqQe.js → shared-report-tRQV1iJl.js} +1 -1
- package/dist/web/assets/{useApi-CROJJdhE-raNlHRDl.js → useApi-CROJJdhE-QfNm4ADP.js} +1 -1
- package/dist/web/assets/{useModalDialog-CroGk2Cs.js → useModalDialog-BopF-KHN.js} +1 -1
- package/dist/web/assets/{vue-router-JFYBLXB1.js → vue-router-COCNAJJn.js} +1 -1
- package/dist/web/index.html +10 -10
- package/docs/agent-conversations-and-collaboration.md +18 -1
- package/docs/agent-interface-architecture.md +17 -0
- package/docs/employee-agents.md +14 -1
- package/docs/project-agent-memory.md +6 -1
- package/graph-provider/fuli_graph/knowledge_search.py +8 -16
- package/graph-provider/fuli_graph/project_agent_routes.py +5 -1
- package/graph-provider/fuli_graph/store_project_agent_context.py +18 -1
- package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +86 -11
- package/graph-provider/pyproject.toml +1 -1
- package/npm-shrinkwrap.json +15 -15
- package/package.json +4 -4
- package/skills/capturing-session-knowledge/SKILL.md +20 -2
- package/skills/operating-fuli/SKILL.md +8 -0
- package/src/agent-tools/employee-definitions.js +6 -5
- package/src/agent-tools/graph-definitions.js +2 -2
- package/src/agents/identity-receipt.js +29 -0
- package/src/conversations/task-context-view.js +3 -1
- package/src/employees/service.js +17 -3
- package/src/employees/task-entry.js +5 -1
- package/src/graphiti/agent-conversations.js +15 -1
- package/src/graphiti/agent-knowledge-workflows.js +1 -1
- package/src/graphiti/federated-application.js +6 -40
- package/src/graphiti/project-agent-mention.js +35 -0
- package/src/graphiti/project-agent-task-entry.js +22 -3
- package/src/graphiti/project-agent-workflows.js +13 -2
- package/src/graphiti/project-path-context.js +17 -10
- package/src/graphiti/task-entry-preferences.js +50 -0
- package/src/mcp/create-mcp-server.js +6 -1
- package/src/mcp/employee-tool-catalog.js +22 -0
- package/src/mcp/tool-result.js +3 -1
- package/dist/web/assets/KnowledgePage-uXW1Eovz.js +0 -1
- package/dist/web/assets/PersonalProjectsPage-Br1S8ETM.js +0 -1
- package/dist/web/assets/ProjectAgentsPage-CZWWV4Kw.css +0 -1
- package/dist/web/assets/index-MKmHNBtc.js +0 -2
- package/src/mcp/task-context-registry.js +0 -165
|
@@ -8,6 +8,16 @@ from .project_agent_access import authorize_project_agent
|
|
|
8
8
|
from .project_agent_models import ProjectAgentProfile
|
|
9
9
|
from .project_agent_task_models import ProjectAgentParallelPlan
|
|
10
10
|
from .store_project_agents import SYSTEM_HR_AGENT_ID
|
|
11
|
+
from .system_hr_identity import LEGACY_HR_AGENT_ID
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
IMPLICIT_PEER_AGENT_IDS = frozenset({
|
|
15
|
+
'employee.jefa',
|
|
16
|
+
SYSTEM_HR_AGENT_ID.casefold(),
|
|
17
|
+
LEGACY_HR_AGENT_ID.casefold(),
|
|
18
|
+
})
|
|
19
|
+
IMPLICIT_PEER_CAPABILITIES = frozenset({'fuli.employee:jefa', 'fuli.employee:bole'})
|
|
20
|
+
IMPLICIT_PEER_AGENT_TYPES = frozenset({'hr'})
|
|
11
21
|
|
|
12
22
|
|
|
13
23
|
class StoreProjectAgentTaskStaffing:
|
|
@@ -79,10 +89,14 @@ class StoreProjectAgentTaskStaffing:
|
|
|
79
89
|
eligible = [row for row in rows if required.issubset({
|
|
80
90
|
item.casefold() for item in row.get('capabilities', [])
|
|
81
91
|
})]
|
|
82
|
-
|
|
92
|
+
implicit_eligible = [
|
|
93
|
+
row for row in eligible
|
|
94
|
+
if self._implicit_owner_allowed(row, request)
|
|
95
|
+
]
|
|
96
|
+
candidates, history = await self._rank_agent_candidates(implicit_eligible, request)
|
|
83
97
|
# Continuity may break a tie between qualified specialists; it must
|
|
84
98
|
# never override a capability requirement or a stronger work-kind fit.
|
|
85
|
-
continuity_pool =
|
|
99
|
+
continuity_pool = implicit_eligible
|
|
86
100
|
if candidates:
|
|
87
101
|
best_work_match = normalized_work_kind in {
|
|
88
102
|
item.casefold() for item in candidates[0].get('work_kinds', [])
|
|
@@ -100,7 +114,10 @@ class StoreProjectAgentTaskStaffing:
|
|
|
100
114
|
)
|
|
101
115
|
if continuity:
|
|
102
116
|
selected = continuity[0]
|
|
103
|
-
remaining = [
|
|
117
|
+
remaining = [
|
|
118
|
+
row for row in candidates or implicit_eligible
|
|
119
|
+
if row['agent_id'] != selected['agent_id']
|
|
120
|
+
]
|
|
104
121
|
return [selected], [selected, *remaining], 'project_continuity', [
|
|
105
122
|
self._project_history_match_basis(
|
|
106
123
|
project_history.get(selected['agent_id']),
|
|
@@ -129,20 +146,20 @@ class StoreProjectAgentTaskStaffing:
|
|
|
129
146
|
'matching Agent is unavailable to source client: '
|
|
130
147
|
f'{request.source_application or "other"}',
|
|
131
148
|
]
|
|
132
|
-
if len(eligible) == 1:
|
|
133
|
-
return [
|
|
149
|
+
if len(eligible) == 1 and len(implicit_eligible) == 1:
|
|
150
|
+
return [implicit_eligible[0]], implicit_eligible, 'sole_active_assignment', [
|
|
134
151
|
'the project has one active Agent assignment',
|
|
135
|
-
*self._automatic_selection_basis(
|
|
152
|
+
*self._automatic_selection_basis(implicit_eligible[0], request),
|
|
136
153
|
]
|
|
137
|
-
if
|
|
138
|
-
|
|
154
|
+
if implicit_eligible and normalized_work_kind == 'project_context' and not required:
|
|
155
|
+
implicit_eligible.sort(key=lambda row: (
|
|
139
156
|
self._metric_int(row.get('active_task_count')),
|
|
140
157
|
-int(self._metric_int(row.get('memory_revision')) > 0),
|
|
141
158
|
row['assigned_at'], row['agent_id'],
|
|
142
159
|
))
|
|
143
|
-
return
|
|
144
|
-
f'{len(
|
|
145
|
-
*self._automatic_selection_basis(
|
|
160
|
+
return implicit_eligible[:1], implicit_eligible, 'project_default', [
|
|
161
|
+
f'{len(implicit_eligible)} eligible project roles; working-memory continuity and stable tie-break',
|
|
162
|
+
*self._automatic_selection_basis(implicit_eligible[0], request),
|
|
146
163
|
]
|
|
147
164
|
return [], rows, 'no_match', ['no active assignment matched exactly']
|
|
148
165
|
selected = candidates[0]
|
|
@@ -166,6 +183,64 @@ class StoreProjectAgentTaskStaffing:
|
|
|
166
183
|
if item not in match_basis)
|
|
167
184
|
return [selected], candidates, reason, match_basis
|
|
168
185
|
|
|
186
|
+
@classmethod
|
|
187
|
+
def _implicit_owner_allowed(cls, row, request):
|
|
188
|
+
"""Keep management peers out of unrelated automatic ownership paths."""
|
|
189
|
+
|
|
190
|
+
if not cls._is_implicit_peer(row):
|
|
191
|
+
return True
|
|
192
|
+
profile = row.get('profile')
|
|
193
|
+
work_kinds = {
|
|
194
|
+
str(item).casefold()
|
|
195
|
+
for item in row.get('work_kinds', [])
|
|
196
|
+
}
|
|
197
|
+
work_kinds.update(
|
|
198
|
+
str(item).casefold()
|
|
199
|
+
for item in (cls._profile_value(profile, 'work_kinds', []) or [])
|
|
200
|
+
)
|
|
201
|
+
capabilities = {
|
|
202
|
+
str(item).casefold()
|
|
203
|
+
for item in row.get('capabilities', [])
|
|
204
|
+
}
|
|
205
|
+
capabilities.update(
|
|
206
|
+
str(item).casefold()
|
|
207
|
+
for item in (cls._profile_value(profile, 'capabilities', []) or [])
|
|
208
|
+
)
|
|
209
|
+
required = {
|
|
210
|
+
str(item).casefold()
|
|
211
|
+
for item in request.required_capabilities
|
|
212
|
+
}
|
|
213
|
+
return request.work_kind.casefold() in work_kinds or (
|
|
214
|
+
bool(required) and required.issubset(capabilities)
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
@classmethod
|
|
218
|
+
def _is_implicit_peer(cls, row):
|
|
219
|
+
agent_id = str(row.get('agent_id') or '').casefold()
|
|
220
|
+
profile = row.get('profile')
|
|
221
|
+
agent_type = str(cls._profile_value(profile, 'agent_type', '')).casefold()
|
|
222
|
+
capabilities = {
|
|
223
|
+
str(item).casefold()
|
|
224
|
+
for item in (cls._profile_value(profile, 'capabilities', []) or [])
|
|
225
|
+
}
|
|
226
|
+
capabilities.update(
|
|
227
|
+
str(item).casefold()
|
|
228
|
+
for item in (row.get('capabilities', []) or [])
|
|
229
|
+
)
|
|
230
|
+
return (
|
|
231
|
+
agent_id in IMPLICIT_PEER_AGENT_IDS
|
|
232
|
+
or agent_type in IMPLICIT_PEER_AGENT_TYPES
|
|
233
|
+
or bool(capabilities & IMPLICIT_PEER_CAPABILITIES)
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
@staticmethod
|
|
237
|
+
def _profile_value(profile, field, default):
|
|
238
|
+
if profile is None:
|
|
239
|
+
return default
|
|
240
|
+
if isinstance(profile, dict):
|
|
241
|
+
return profile.get(field, default)
|
|
242
|
+
return getattr(profile, field, default)
|
|
243
|
+
|
|
169
244
|
def _automatic_selection_basis(self, selected, request):
|
|
170
245
|
work_kind = request.work_kind.casefold()
|
|
171
246
|
if work_kind == 'project_context':
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fuli-context",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "fuli-context",
|
|
9
|
-
"version": "0.8.
|
|
9
|
+
"version": "0.8.3",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@hono/node-server": "2.0.12",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@types/node": "^24.13.3",
|
|
23
23
|
"@vitejs/plugin-vue": "^6.0.8",
|
|
24
24
|
"@vue/test-utils": "^2.4.11",
|
|
25
|
-
"brace-expansion": "5.0.
|
|
25
|
+
"brace-expansion": "5.0.12",
|
|
26
26
|
"d3": "^7.9.0",
|
|
27
27
|
"jsdom": "^29.1.1",
|
|
28
28
|
"linkedom": "^0.18.12",
|
|
@@ -1867,9 +1867,9 @@
|
|
|
1867
1867
|
"license": "ISC"
|
|
1868
1868
|
},
|
|
1869
1869
|
"node_modules/brace-expansion": {
|
|
1870
|
-
"version": "5.0.
|
|
1871
|
-
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.
|
|
1872
|
-
"integrity": "sha512-
|
|
1870
|
+
"version": "5.0.12",
|
|
1871
|
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz",
|
|
1872
|
+
"integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==",
|
|
1873
1873
|
"dev": true,
|
|
1874
1874
|
"license": "MIT",
|
|
1875
1875
|
"dependencies": {
|
|
@@ -2949,9 +2949,9 @@
|
|
|
2949
2949
|
"license": "MIT"
|
|
2950
2950
|
},
|
|
2951
2951
|
"node_modules/fast-uri": {
|
|
2952
|
-
"version": "3.1.
|
|
2953
|
-
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.
|
|
2954
|
-
"integrity": "sha512-
|
|
2952
|
+
"version": "3.1.8",
|
|
2953
|
+
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz",
|
|
2954
|
+
"integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==",
|
|
2955
2955
|
"funding": [
|
|
2956
2956
|
{
|
|
2957
2957
|
"type": "github",
|
|
@@ -3285,9 +3285,9 @@
|
|
|
3285
3285
|
}
|
|
3286
3286
|
},
|
|
3287
3287
|
"node_modules/ip-address": {
|
|
3288
|
-
"version": "10.
|
|
3289
|
-
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.
|
|
3290
|
-
"integrity": "sha512-
|
|
3288
|
+
"version": "10.7.2",
|
|
3289
|
+
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz",
|
|
3290
|
+
"integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==",
|
|
3291
3291
|
"license": "MIT",
|
|
3292
3292
|
"engines": {
|
|
3293
3293
|
"node": ">= 12"
|
|
@@ -5077,9 +5077,9 @@
|
|
|
5077
5077
|
"license": "ISC"
|
|
5078
5078
|
},
|
|
5079
5079
|
"node_modules/undici": {
|
|
5080
|
-
"version": "7.
|
|
5081
|
-
"resolved": "https://registry.npmjs.org/undici/-/undici-7.
|
|
5082
|
-
"integrity": "sha512-
|
|
5080
|
+
"version": "7.30.0",
|
|
5081
|
+
"resolved": "https://registry.npmjs.org/undici/-/undici-7.30.0.tgz",
|
|
5082
|
+
"integrity": "sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==",
|
|
5083
5083
|
"dev": true,
|
|
5084
5084
|
"license": "MIT",
|
|
5085
5085
|
"engines": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fuli-context",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "Local-first collaboration relationship graph for AI agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"type": "git",
|
|
66
66
|
"url": "git+https://github.com/JevonsCode/fuli.git"
|
|
67
67
|
},
|
|
68
|
-
"homepage": "https://github.
|
|
68
|
+
"homepage": "https://jevonscode.github.io/fuli/",
|
|
69
69
|
"bugs": {
|
|
70
70
|
"url": "https://github.com/JevonsCode/fuli/issues"
|
|
71
71
|
},
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"@types/node": "^24.13.3",
|
|
84
84
|
"@vitejs/plugin-vue": "^6.0.8",
|
|
85
85
|
"@vue/test-utils": "^2.4.11",
|
|
86
|
-
"brace-expansion": "5.0.
|
|
86
|
+
"brace-expansion": "5.0.12",
|
|
87
87
|
"d3": "^7.9.0",
|
|
88
88
|
"jsdom": "^29.1.1",
|
|
89
89
|
"linkedom": "^0.18.12",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"@hono/node-server": "2.0.12"
|
|
103
103
|
},
|
|
104
104
|
"brace-expansion": "$brace-expansion",
|
|
105
|
-
"fast-uri": "3.1.
|
|
105
|
+
"fast-uri": "3.1.8",
|
|
106
106
|
"qs": "6.16.0"
|
|
107
107
|
}
|
|
108
108
|
}
|
|
@@ -9,6 +9,13 @@ Accumulate reusable knowledge without interrupting the user's normal work. Keep
|
|
|
9
9
|
|
|
10
10
|
## Establish Task Context
|
|
11
11
|
|
|
12
|
+
For a coordinated worker sharing its parent's host task, use the isolated context and lifecycle
|
|
13
|
+
policy supplied by the coordinator. An inherited task token belongs to the parent: do not call
|
|
14
|
+
`begin_task_context`, `checkpoint_task_knowledge`, or write the parent's Agent memory. Return
|
|
15
|
+
your result, evidence and proposed work summary to the coordinator for integration. Worker
|
|
16
|
+
activity uses `record_project_agent_task_activity`. A separately created host task/session has
|
|
17
|
+
its own lifecycle and follows the normal entry and checkpoint rules below.
|
|
18
|
+
|
|
12
19
|
At the start of every user task, first use the lifecycle context supplied by the host when one
|
|
13
20
|
exists. A Claude Code `UserPromptSubmit` hook calls `begin_task_context`, which already loads
|
|
14
21
|
collaboration preferences and resolves the exact local personal project from the current working
|
|
@@ -230,9 +237,11 @@ project history. Explicit user-selected Agents and locked policies remain higher
|
|
|
230
237
|
Treat the returned durable Agent identity and a host worker as different things. A durable Agent
|
|
231
238
|
keeps its project assignment, preferences, task history, and Agent-scoped graph memory while idle;
|
|
232
239
|
the host creates an execution process only when needed. Start only entries in `worker_plan`, give
|
|
233
|
-
each worker only its returned isolated `context
|
|
240
|
+
each worker only its returned isolated `context` (including `lifecycle`), role, and workstream boundary, and never expose
|
|
234
241
|
another Agent's private context. Fuli is the control plane and does not itself prove that a native
|
|
235
242
|
worker started.
|
|
243
|
+
Do not forward the parent's task token. The coordinator alone checkpoints the shared host task
|
|
244
|
+
after integrating worker results; a worker's completion is not that task's completion.
|
|
236
245
|
|
|
237
246
|
The host adapter must keep an adaptive-runtime graph lease for the whole worker run, and an
|
|
238
247
|
executor lease when Fuli manages that executor. Call `acquire_runtime_lease` before starting
|
|
@@ -243,6 +252,14 @@ including concrete worker identity. Call `record_project_agent_executor_actual`
|
|
|
243
252
|
executor/model use. If the host cannot start a worker, report the task as blocked or failed and do
|
|
244
253
|
not fabricate an execution-summary row. The Provider's `executionSummary` remains authoritative.
|
|
245
254
|
|
|
255
|
+
`workerStatus` requires `workerId` and the participating `agentId`. Structured actual executor,
|
|
256
|
+
provider and model form one report and must match authorized preflight evidence. When that
|
|
257
|
+
evidence is missing or inconsistent, omit all three; preserve the observed `workerRuntime`,
|
|
258
|
+
terminal status and mismatch in the summary. Do not guess a provider or rewrite preflight to
|
|
259
|
+
make history fit. Valid terminal activity automatically creates its routing outcome. Additional
|
|
260
|
+
user/test outcome evidence needs a matching recorded execution, the task's exact work kind and
|
|
261
|
+
model strategy, and real references; a CLI session ID alone does not establish that execution.
|
|
262
|
+
|
|
246
263
|
Keep per-worker completion separate from the global task transition. While parallel work is still
|
|
247
264
|
being integrated, report each worker's terminal result in `workerStatus` and keep the task status
|
|
248
265
|
`running`; this preserves one terminal execution-summary row per actual worker. Only after every
|
|
@@ -388,7 +405,8 @@ If `capture_session_knowledge` or automatic project creation returns `capture_di
|
|
|
388
405
|
user has disabled automatic capture. Do not retry, do not create a fallback record elsewhere,
|
|
389
406
|
and do not describe the skipped batch as stored. Existing knowledge may still be queried.
|
|
390
407
|
|
|
391
|
-
Before sending the final response, perform one checkpoint.
|
|
408
|
+
Before sending the final response for a task you own, perform one checkpoint. A coordinated
|
|
409
|
+
worker returns its result to the owner instead of checkpointing an inherited token. For a hook-managed task, call
|
|
392
410
|
`checkpoint_task_knowledge` exactly once with either `capture_candidates` and the bounded durable
|
|
393
411
|
batch, or `retain_nothing`. For a prompt-only Agent, either flush the durable batch directly or
|
|
394
412
|
determine that the turn produced no reusable knowledge. Do not finish a reusable project decision
|
|
@@ -39,6 +39,10 @@ For read-only questions, use the narrowest list/get/search tool and return sourc
|
|
|
39
39
|
|
|
40
40
|
When the user addresses HR or Bole to ask about organization or find someone, resolve the existing dedicated HR identity and read `list_project_agents` plus `get_project_agent_coordination_policy` for the exact personal space and relevant project. Use recorded roles, assignments and team membership; do not invent an organization or recruit a replacement HR. Match names, responsibilities and capabilities, but never guess between ambiguous or duplicate names. Link each resolved name to its personal page at `/agents/{encodedPersonalSpaceId}/{encodedAgentId}`. Answering in the HR role does not mean a worker has started; report actual execution separately.
|
|
41
41
|
|
|
42
|
+
## Before starting collaborators
|
|
43
|
+
|
|
44
|
+
Read each returned worker's `context.agent.profile.allowedClients` and `executorPolicy`, together with the task's effective executor policy. Intersect the restrictions before starting either a native or external worker; the lead's selected executor does not authorize every collaborator. A locked allow-list applies even when the current host has an idle worker. If the intended platform is outside any effective lock, select an eligible Agent/executor or report the constraint before launch. Never relax the lock, relabel the worker, or invent model evidence to satisfy reporting. FULI context recovery and a ready worker plan are not execution authorization by themselves.
|
|
45
|
+
|
|
42
46
|
## Handling conflicts and failures
|
|
43
47
|
|
|
44
48
|
- On revision or timestamp conflict, re-read, explain what changed, and recompute the intended patch. Do not blindly retry stale input.
|
|
@@ -49,3 +53,7 @@ When the user addresses HR or Bole to ask about organization or find someone, re
|
|
|
49
53
|
## Completion response
|
|
50
54
|
|
|
51
55
|
State what changed, the exact project or item scope, the verification result, and any remaining human-only action. Do not claim success from a configured capability or submitted request alone.
|
|
56
|
+
|
|
57
|
+
Include the task's named durable owner and profile link from `agent_receipt` or the successful checkpoint's `conversation.receipt`. Include Jefa or other peers only with an actual `collaboration_receipt`, naming the tool work they performed; board tools do not imply a separate model worker ran. Keep this receipt brief and distinguish work completion, memory checkpoint and visible-transcript coverage. Do not report saved memory when capture failed or is disabled.
|
|
58
|
+
|
|
59
|
+
For cross-client continuation, copy the supplied continuation prompt into the same project's connected client. A leading `@Name` matches exact directory names; `@{agentId}` is the stable form (the generated prompt percent-encodes unusual IDs). Duplicate names require an exact ID in the current request; an ambiguous explicit mention must not fall back to the prior session owner or memory. This is FULI task-entry matching, not registration in a client's native @ menu. Obtain a fresh task token before `resume_agent_conversation`; never copy an old token between clients. If no qualified owner exists, route staffing through HR before implementation; Jefa remains a management peer. Preserve confirmed taste/personality/preferences and Agent character/expectations in delegated context.
|
|
@@ -3,6 +3,7 @@ import { booleanSchema, objectSchema, stringSchema } from './schema.js';
|
|
|
3
3
|
const id = { ...stringSchema(), minLength: 1, maxLength: 256 };
|
|
4
4
|
const projectPath = { ...stringSchema(), minLength: 1, maxLength: 4096 };
|
|
5
5
|
const target = { projectPath, templateId: id };
|
|
6
|
+
const workbenchTarget = { ...target, personalProjectId: id };
|
|
6
7
|
|
|
7
8
|
export const EMPLOYEE_TOOL_DEFINITIONS = [
|
|
8
9
|
{
|
|
@@ -29,15 +30,15 @@ export const EMPLOYEE_TOOL_DEFINITIONS = [
|
|
|
29
30
|
},
|
|
30
31
|
{
|
|
31
32
|
name: 'list_employee_tools', title: 'READ · Employee workbench tools',
|
|
32
|
-
description: 'Discover
|
|
33
|
-
inputSchema: objectSchema(
|
|
33
|
+
description: 'Discover complete input schemas and permitted tools for a recruited employee. Supply projectPath, or a previously resolved personalProjectId without projectPath when directory resolution is unavailable. If both are supplied they must resolve to the same project. Existing project and employee authorization always apply. Use includeSchemas=false for a compact name list, then toolName to read one exact schema. Oversized contracts fail explicitly, never return partial schemas. Read the schema before calling; a workbench has to be installed first.',
|
|
34
|
+
inputSchema: objectSchema({ ...workbenchTarget, toolName: id, includeSchemas: booleanSchema() }, ['templateId'])
|
|
34
35
|
},
|
|
35
36
|
{
|
|
36
37
|
name: 'call_employee_tool', title: 'WRITE · Call an employee workbench tool',
|
|
37
|
-
description: 'Call a discovered employee tool inside the exact assigned project. May read or change data according to the tool schema. Preserve optimistic concurrency and idempotency keys. Does not grant cross-project access, execute an external model, or confirm task completion for the human.',
|
|
38
|
+
description: 'Call a discovered employee tool inside the exact assigned project. Supply projectPath or a previously resolved personalProjectId; omit projectPath if directory resolution is unavailable. If both are supplied they must resolve to the same project. May read or change data according to the tool schema. Preserve optimistic concurrency and idempotency keys. Does not grant cross-project access, execute an external model, or confirm task completion for the human.',
|
|
38
39
|
inputSchema: objectSchema({
|
|
39
|
-
...
|
|
40
|
+
...workbenchTarget, tool: id,
|
|
40
41
|
arguments: { type: 'object', additionalProperties: true }
|
|
41
|
-
}, ['
|
|
42
|
+
}, ['templateId', 'tool', 'arguments'])
|
|
42
43
|
}
|
|
43
44
|
];
|
|
@@ -806,7 +806,7 @@ export const GRAPH_TOOL_DEFINITIONS = [
|
|
|
806
806
|
{
|
|
807
807
|
name: 'record_project_agent_task_activity',
|
|
808
808
|
title: 'WRITE · Record task activity',
|
|
809
|
-
description: 'Record
|
|
809
|
+
description: 'Record real task/worker activity and optional source-labelled cumulative token usage; never estimate. workerStatus requires workerId and agentId. Report actualExecutorId, actualModelProvider and actualModel together only with matching authorized preflight evidence. Otherwise omit all three, report observed workerRuntime and explain the mismatch in summary. A terminal activity with valid actual execution automatically records its routing outcome.',
|
|
810
810
|
inputSchema: projectAgentTaskActivityInput
|
|
811
811
|
},
|
|
812
812
|
{
|
|
@@ -1016,7 +1016,7 @@ export const GRAPH_TOOL_DEFINITIONS = [
|
|
|
1016
1016
|
{
|
|
1017
1017
|
name: 'record_project_agent_task_outcome',
|
|
1018
1018
|
title: 'WRITE · Record explicit routing outcome evidence',
|
|
1019
|
-
description: 'Record
|
|
1019
|
+
description: 'Record explicit user/test outcome evidence against a matching recorded execution. Use the task workKind and effective modelStrategy plus real referenceIds; a CLI session ID alone is not a recorded run. Terminal routing outcomes are already derived from valid task activity; do not submit them again. Never infer satisfaction or fabricate execution evidence to clear a validation error.',
|
|
1020
1020
|
inputSchema: projectAgentTaskOutcomeInput
|
|
1021
1021
|
},
|
|
1022
1022
|
{
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { sourceConsoleUrl } from '../graphiti/source-marker.js';
|
|
2
|
+
|
|
3
|
+
export function agentProfileUrl(application, agentId) {
|
|
4
|
+
return `${application.consoleUrl ?? sourceConsoleUrl(null)}/agents/${encodeURIComponent(application.config.personal.spaceId)}/${encodeURIComponent(agentId)}`;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function agentContinuation(projectId, agentId, conversationId = null) {
|
|
8
|
+
return { personal_project_id: projectId, agent_id: agentId,
|
|
9
|
+
...(conversationId ? { conversation_id: conversationId } : {}),
|
|
10
|
+
prompt: `@{${encodeURIComponent(agentId)}} 请在 FULI 项目 ${JSON.stringify(projectId)} 对应的工作目录继续工作。`
|
|
11
|
+
+ (conversationId ? `先用当前任务的 taskContextToken 调用 resume_agent_conversation,conversationId=${JSON.stringify(conversationId)},再按需读取上下文。` : '先恢复这个 Agent 的项目记忆和最近会话。'),
|
|
12
|
+
guidance: 'Use a connected client and the same authorized project/Agent. Obtain a fresh task context; never reuse the previous client’s token. This is a FULI prompt, not a native client @ picker registration.' };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function taskAgentReceipt(application, { projectId, agentId, name = null,
|
|
16
|
+
sourceApplication = null, workStatus = null, conversationId = null, persistence = null }) {
|
|
17
|
+
return { owner: { agent_id: agentId, name,
|
|
18
|
+
profile_url: agentProfileUrl(application, agentId), source_application: sourceApplication },
|
|
19
|
+
personal_project_id: projectId, work_status: workStatus, persistence,
|
|
20
|
+
continuation: agentContinuation(projectId, agentId, conversationId),
|
|
21
|
+
guidance: 'Finish with the named owner and profile link, truthful work/memory status and continuation. Add only actually used collaboration_receipt participants (for example Jefa); tools and configured peers are not independent model workers.' };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function employeeCollaborationReceipt(application, context, { tool, permission, sourceApplication }) {
|
|
25
|
+
return { agent_id: context.agentId, name: context.name, role: context.role,
|
|
26
|
+
profile_url: agentProfileUrl(application, context.agentId),
|
|
27
|
+
personal_project_id: context.project.id, tool, permission,
|
|
28
|
+
status: 'tool_completed', source_application: sourceApplication ?? 'other', worker_started: false };
|
|
29
|
+
}
|
|
@@ -5,7 +5,9 @@ export function compactTaskContext(value) {
|
|
|
5
5
|
const result = { ...value };
|
|
6
6
|
if (Array.isArray(value.effective_preferences)) result.effective_preferences = value.effective_preferences.map(item => ({
|
|
7
7
|
instruction: item.instruction, preference_key: item.preference_key,
|
|
8
|
-
preference_scope: item.preference_scope, confirmation_status: item.confirmation_status
|
|
8
|
+
preference_scope: item.preference_scope, confirmation_status: item.confirmation_status,
|
|
9
|
+
profile_aspect: item.profile_aspect,
|
|
10
|
+
...(item.attributes?.tasteDomain ? { attributes: { tasteDomain: item.attributes.tasteDomain } } : {})
|
|
9
11
|
}));
|
|
10
12
|
const continuity = value.project_agent_context;
|
|
11
13
|
if (continuity?.memory) {
|
package/src/employees/service.js
CHANGED
|
@@ -4,6 +4,7 @@ import { createEmployeeRecruitment } from './recruitment.js';
|
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { EmployeeManagementStore } from './management-policy.js';
|
|
6
6
|
import { employeeTaskEntry } from './task-entry.js';
|
|
7
|
+
import { employeeCollaborationReceipt } from '../agents/identity-receipt.js';
|
|
7
8
|
|
|
8
9
|
export function createEmployeeService({ app, runtimeConfigPath, registry: configuredRegistry, managementStore: configuredManagementStore }) {
|
|
9
10
|
const registry = configuredRegistry ?? createEmployeePackageRegistry(employeeDirectories(runtimeConfigPath));
|
|
@@ -39,7 +40,14 @@ export function createEmployeeService({ app, runtimeConfigPath, registry: config
|
|
|
39
40
|
async function describeTools(input) {
|
|
40
41
|
const context = await workspace(input);
|
|
41
42
|
const runtime = await registry.runtime(context.templateId);
|
|
42
|
-
|
|
43
|
+
const { manifest } = registry.get(context.templateId);
|
|
44
|
+
let tools = (await runtime.describeTools()).filter(tool => manifest.permissions.includes(tool.permission));
|
|
45
|
+
if (input.toolName) {
|
|
46
|
+
tools = tools.filter(tool => tool.name === input.toolName);
|
|
47
|
+
if (!tools.length) throw new EmployeeError('Employee tool is not available or permitted', 403, 'tool_not_permitted');
|
|
48
|
+
}
|
|
49
|
+
if (input.includeSchemas === false) tools = tools.map(({ inputSchema, ...tool }) => tool);
|
|
50
|
+
return { ...context, tools };
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
async function callTool(input) {
|
|
@@ -58,7 +66,13 @@ export function createEmployeeService({ app, runtimeConfigPath, registry: config
|
|
|
58
66
|
sourceApplication: input.sourceApplication ?? 'other', id: input.sourceSessionId,
|
|
59
67
|
verified: input.sourceSessionVerified === true,
|
|
60
68
|
} : null;
|
|
61
|
-
try {
|
|
69
|
+
try {
|
|
70
|
+
const result = await runtime.callTool(input.tool, input.arguments ?? {}, { ...context, session });
|
|
71
|
+
if (!result || typeof result !== 'object' || Array.isArray(result)) return result;
|
|
72
|
+
return { ...result, collaboration_receipt: employeeCollaborationReceipt(app, context, {
|
|
73
|
+
tool: input.tool, permission: tool.permission, sourceApplication: input.sourceApplication
|
|
74
|
+
}) };
|
|
75
|
+
}
|
|
62
76
|
catch (error) {
|
|
63
77
|
if (Number.isInteger(error?.status) && error.status >= 400 && error.status < 500) {
|
|
64
78
|
throw new EmployeeError(error.message, error.status, error.code ?? 'tool_error');
|
|
@@ -72,7 +86,7 @@ export function createEmployeeService({ app, runtimeConfigPath, registry: config
|
|
|
72
86
|
recruit: async (input) => recruitment.recruit(await resolveInput(input)),
|
|
73
87
|
workspace, describeTools, callTool,
|
|
74
88
|
authorize: recruitment.authorize,
|
|
75
|
-
taskEntry: (input) => employeeTaskEntry({ registry, recruitment, workspace }, input)
|
|
89
|
+
taskEntry: (input) => employeeTaskEntry({ app, registry, recruitment, workspace }, input)
|
|
76
90
|
.catch(() => ({ status: 'unavailable', managers: [], worker_started: false })),
|
|
77
91
|
async handleHttp(request, response, input) {
|
|
78
92
|
const context = await workspace(input);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Management is a task-entry capability, not a replacement for the specialist
|
|
2
2
|
// role/executor chosen by FULI. Never persist the user's prompt or start a model.
|
|
3
|
-
|
|
3
|
+
import { employeeCollaborationReceipt } from '../agents/identity-receipt.js';
|
|
4
|
+
export async function employeeTaskEntry({ app, registry, recruitment, workspace }, input) {
|
|
4
5
|
if (!input.personalProjectId) return { status: 'project_unresolved', managers: [], worker_started: false };
|
|
5
6
|
const managers = [];
|
|
6
7
|
let unavailable = false;
|
|
@@ -18,6 +19,9 @@ export async function employeeTaskEntry({ registry, recruitment, workspace }, in
|
|
|
18
19
|
const board = await runtime.callTool(manifest.taskEntry.boardTool, { limit: 8 }, resolved);
|
|
19
20
|
managers.push({
|
|
20
21
|
template_id: manifest.id, agent_id: context.agentId, name: manifest.name,
|
|
22
|
+
collaboration_receipt: employeeCollaborationReceipt(app, resolved, {
|
|
23
|
+
tool: manifest.taskEntry.boardTool, permission: 'board.read', sourceApplication: input.sourceApplication
|
|
24
|
+
}),
|
|
21
25
|
personal_project_id: context.project.id, scope_mode: context.management.mode,
|
|
22
26
|
title_mode: context.management.titleMode, title_style: context.management.titleStyle,
|
|
23
27
|
board: { version: board.version, total: board.total, truncated: board.truncated,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { detectSensitiveContent } from '../security/sensitive-content.js';
|
|
2
|
+
import { agentContinuation, taskAgentReceipt } from '../agents/identity-receipt.js';
|
|
2
3
|
|
|
3
4
|
export function conversationScope(application, input) {
|
|
4
5
|
return { personal_space_id: application.config.personal.spaceId,
|
|
@@ -46,16 +47,29 @@ export async function checkpointConversation(application, task, workLog) {
|
|
|
46
47
|
...conversationTask(application, task), summary: safeConversationContent(workLog.summary), status: workLog.status,
|
|
47
48
|
events: [{ event_id: `${task.token}:summary`, role: 'summary', kind: 'checkpoint', content: safeConversationContent(workLog.summary) }]
|
|
48
49
|
});
|
|
50
|
+
if (result.status !== 'saved') return result;
|
|
51
|
+
let name = null;
|
|
52
|
+
try {
|
|
53
|
+
const agent = await application.personal.getProjectAgent(application.config.personal.spaceId, task.personalProjectId, task.projectAgentId);
|
|
54
|
+
name = agent?.profile?.display_name || agent?.profile?.name || null;
|
|
55
|
+
} catch { /* Metadata availability does not undo a successful save. */ }
|
|
49
56
|
return { ...result, agent_id: task.projectAgentId, work_status: workLog.status,
|
|
57
|
+
receipt: taskAgentReceipt(application, { projectId: task.personalProjectId,
|
|
58
|
+
agentId: task.projectAgentId, name, sourceApplication: task.sourceApplication,
|
|
59
|
+
workStatus: workLog.status, conversationId: result.conversation_id,
|
|
60
|
+
persistence: result.status }),
|
|
50
61
|
coverage: 'task_summary; visible transcript capture is confirmed separately by the host hook',
|
|
51
62
|
guidance: 'Include the Agent name, truthful work status and conversation ID in the final receipt. In another client select this Agent, then call resume_agent_conversation with its current taskContextToken and this conversation ID.' };
|
|
52
63
|
} catch { return unavailable(); }
|
|
53
64
|
}
|
|
54
65
|
|
|
55
66
|
export async function queryConversations(application, input) {
|
|
56
|
-
|
|
67
|
+
const result = await application.personal.conversation('query', { ...conversationScope(application, input),
|
|
57
68
|
mode: input.mode ?? 'list', ...(input.conversationId ? { conversation_id: input.conversationId } : {}),
|
|
58
69
|
after: input.after ?? 0, limit: input.limit ?? (input.mode === 'events' ? 3 : 10) });
|
|
70
|
+
if (!Array.isArray(result.conversations)) return result;
|
|
71
|
+
return { ...result, conversations: result.conversations.map(conversation => ({ ...conversation,
|
|
72
|
+
continuation_prompt: agentContinuation(input.personalProjectId, input.agentId, conversation.id).prompt })) };
|
|
59
73
|
}
|
|
60
74
|
export async function resumeConversation(application, input) {
|
|
61
75
|
const task = await application.taskContextRegistry.context(input.taskContextToken, input.sourceApplication);
|
|
@@ -69,7 +69,7 @@ export async function beginTaskContext(application, {
|
|
|
69
69
|
profile_capture: 'Evaluate durable artifact taste (including writing voice, wording, structure and formatting), stable collaboration personality, and decision preferences separately. Do not default every preference to judgment_preference. Use tasteDomain: writing for writing evidence; retain exact session/turn/time provenance. Agent-inferred personality remains pending. Report actual missing evidence rather than promising automatic progress from use alone.',
|
|
70
70
|
human_attention: 'When the selected Project Agent needs a human answer, decision, review or permission, use request_agent_attention with the exact current space/project/Agent IDs, a stable idempotency key and a specific requestedAction. Never raise a hand for ordinary running, queueing or automatic retries. Read list_agent_attention with status=resolved for replies; cancel obsolete requests with cancel_agent_attention. A response does not grant permission or accept task completion.',
|
|
71
71
|
retrieval: 'Inspect task_knowledge_recall before asking for a stable project fact or method again. On a miss, use search_current_project_knowledge with focused action, artifact, target-system, or identifier queries; never use the full conversational request as the only query.',
|
|
72
|
-
checkpoint: 'Before finishing, call checkpoint_task_knowledge with capture_candidates or retain_nothing. For an assigned employee include workLog with a concise result summary and truthful status (completed, incomplete, failed, no_change, or reported), even when there is no knowledge to capture. When durable role context changed, also include agentMemory with the loaded revision and bounded merged notes. Do not overwrite from truncated context or store raw transcripts.'
|
|
72
|
+
checkpoint: 'The task owner checkpoints once after integration. Coordinated workers return results and evidence to the coordinator; they must not begin or checkpoint an inherited parent context. Before finishing the owned task, call checkpoint_task_knowledge with capture_candidates or retain_nothing. For an assigned employee include workLog with a concise result summary and truthful status (completed, incomplete, failed, no_change, or reported), even when there is no knowledge to capture. When durable role context changed, also include agentMemory with the loaded revision and bounded merged notes. Do not overwrite from truncated context or store raw transcripts.'
|
|
73
73
|
}
|
|
74
74
|
};
|
|
75
75
|
}
|
|
@@ -76,11 +76,8 @@ import {
|
|
|
76
76
|
import {
|
|
77
77
|
relatedProjectGuidance
|
|
78
78
|
} from './related-project-suggestions.js';
|
|
79
|
-
import {
|
|
80
|
-
getCollaborationPreferences as getCollaborationPreferencesWorkflow
|
|
81
|
-
} from './collaboration-preference-workflow.js';
|
|
82
79
|
import { buildUserTasteSkill } from './user-taste-skill.js';
|
|
83
|
-
import {
|
|
80
|
+
import { taskEntryPreferences } from './task-entry-preferences.js';
|
|
84
81
|
import { getWritingTasteProfile as getWritingTasteProfileWorkflow } from './writing-taste-profile-workflow.js';
|
|
85
82
|
import {
|
|
86
83
|
groupSubscriptions,
|
|
@@ -256,42 +253,11 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
256
253
|
agentToolName = 'get_collaboration_preferences'
|
|
257
254
|
} = {}) {
|
|
258
255
|
const projectResolution = await this.#resolvePreferenceProject({ personalProjectId, projectPath });
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
? sessionId ?? sourceSessionId : turnId ? sessionId : null,
|
|
265
|
-
turnId: agentToolName === 'begin_task_context' ? turnId : null,
|
|
266
|
-
workKind, requiredCapabilities, agentInvocation, agentToolName }
|
|
267
|
-
);
|
|
268
|
-
// A rejected explicit role must not leak its private preferences via fallback.
|
|
269
|
-
const selectedAgentId = selection ? selection.agent?.agentId ?? null : projectAgentId;
|
|
270
|
-
const preferences = await getCollaborationPreferencesWorkflow(
|
|
271
|
-
this,
|
|
272
|
-
projectResolution,
|
|
273
|
-
{
|
|
274
|
-
projectAgentId: selectedAgentId,
|
|
275
|
-
taskPrompt,
|
|
276
|
-
limit,
|
|
277
|
-
agentInvocation,
|
|
278
|
-
agentToolName
|
|
279
|
-
},
|
|
280
|
-
(items, toolName) => this.#recordAgentViews(items, toolName)
|
|
281
|
-
);
|
|
282
|
-
const management = this.employees && agentInvocation &&
|
|
283
|
-
['begin_task_context', 'get_collaboration_preferences'].includes(agentToolName)
|
|
284
|
-
? await this.employees.taskEntry({ personalProjectId: projectResolution.personalProjectId,
|
|
285
|
-
sourceApplication, sourceSessionId, sessionId })
|
|
286
|
-
: null;
|
|
287
|
-
const managedPreferences = management ? { ...preferences, project_management_context: management } : preferences;
|
|
288
|
-
if (!selection) return managedPreferences;
|
|
289
|
-
const context = selection.agent ? await loadProjectAgentContinuity(this, {
|
|
290
|
-
projectId: projectResolution.personalProjectId, agent: selection.agent,
|
|
291
|
-
sourceApplication, taskPrompt, selectionReason: selection.reason,
|
|
292
|
-
matchBasis: selection.match_basis
|
|
293
|
-
}) : selection;
|
|
294
|
-
return { ...managedPreferences, project_agent_context: context };
|
|
256
|
+
return taskEntryPreferences(this, projectResolution, {
|
|
257
|
+
personalProjectId, projectAgentId, projectPath, taskPrompt, sourceApplication,
|
|
258
|
+
sourceSessionId, sessionId, turnId, workKind, requiredCapabilities,
|
|
259
|
+
limit, agentInvocation, agentToolName
|
|
260
|
+
}, (items, toolName) => this.#recordAgentViews(items, toolName));
|
|
295
261
|
}
|
|
296
262
|
|
|
297
263
|
async getUserTasteSkill({
|