fuli-context 0.7.6 → 0.7.8
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-sLV9WChi.js → AboutPage-9CVX91tr.js} +1 -1
- package/dist/web/assets/{ConnectionsPage-BWn4ywGJ.js → ConnectionsPage-CWbCVFdv.js} +1 -1
- package/dist/web/assets/EmployeeRecruitDialog-CQgNpGJ_.js +1 -0
- package/dist/web/assets/EmployeeRecruitDialog-CfFlyKSh.css +1 -0
- package/dist/web/assets/EmployeeWorkbenchPage-CWg4WJhr.css +1 -0
- package/dist/web/assets/EmployeeWorkbenchPage-CwU4qBgD.js +1 -0
- package/dist/web/assets/KnowledgeEditDialog-B6TGAcFH.js +1 -0
- package/dist/web/assets/{KnowledgeEditDialog-DWUV_NPp.css → KnowledgeEditDialog-iv-dgUeD.css} +1 -1
- package/dist/web/assets/{KnowledgeInspector-CRQWOaos.js → KnowledgeInspector-M2pLY0bR.js} +1 -1
- package/dist/web/assets/{KnowledgeOrganizerPage-Dfj3vIpU.js → KnowledgeOrganizerPage-DzeAlk9b.js} +1 -1
- package/dist/web/assets/KnowledgePage-BJu9w0fF.js +1 -0
- package/dist/web/assets/{KnowledgeWorkspace-ChEB5Bwk.js → KnowledgeWorkspace-CCt8rnBb.js} +1 -1
- package/dist/web/assets/{OverviewPage-Cd66M0RT.js → OverviewPage-CZswVqui.js} +1 -1
- package/dist/web/assets/{PersonalProfilePage-RCFHL0Wz.js → PersonalProfilePage-f1nSF6kx.js} +1 -1
- package/dist/web/assets/PersonalProjectsPage-D__eJf0t.js +1 -0
- package/dist/web/assets/ProjectAgentsPage-C3vW6wmk.css +1 -0
- package/dist/web/assets/ProjectAgentsPage-LrwagMGj.js +11 -0
- package/dist/web/assets/{PublicProjectsPage-mstPEjHe.js → PublicProjectsPage-BdC2Qk4n.js} +1 -1
- package/dist/web/assets/{ReviewPage-6Y1SmfZy.js → ReviewPage-Cbgl8_Si.js} +1 -1
- package/dist/web/assets/SearchableSelect-ERm-k4KI.js +1 -0
- package/dist/web/assets/{SettingsPage--LdTMUm7.js → SettingsPage-DWso4x29.js} +1 -1
- package/dist/web/assets/{WritingTastePage-CIpKswvs.js → WritingTastePage-CFpCjmIY.js} +1 -1
- package/dist/web/assets/catalog-WGLn7ERJ.js +1 -0
- package/dist/web/assets/{console-BT1P2Db4.js → console-DQgu0JIA.js} +1 -1
- package/dist/web/assets/conversation-launcher-settings-0KtlG_LV.js +1 -0
- package/dist/web/assets/{i18n-js6gfc7j.js → i18n-ByRiA3pJ.js} +3 -3
- package/dist/web/assets/{identity-CfDfanpa.js → identity-CxdPRMwH.js} +1 -1
- package/dist/web/assets/index-JAZe6lfX.js +2 -0
- package/dist/web/assets/index-wcXzzggp.css +1 -0
- package/dist/web/assets/{model-DJkLAJsg.js → model-Db4-KefQ.js} +1 -1
- package/dist/web/assets/{useApi-CROJJdhE-BdSti7Y2.js → useApi-CROJJdhE-CePMjc08.js} +1 -1
- package/dist/web/assets/{vue-router-Kk67EaCP.js → vue-router-CzX-hn3P.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_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 +21 -1
- package/graph-provider/fuli_graph/project_agent_task_models.py +217 -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_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 +748 -162
- package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +230 -18
- package/graph-provider/fuli_graph/store_project_agent_tasks.py +261 -468
- 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 +25 -6
- package/src/agent-tools/graph-handlers.js +11 -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/update-command.js +6 -43
- 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 +20 -3
- package/src/graphiti/project-agent-mapping.js +155 -5
- 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 +30 -14
- package/src/graphiti/provider-client.js +66 -4
- package/src/http/api-router.js +30 -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 +2 -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 +10 -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/semantic-version.js +42 -0
- 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/src/system/package-version-checker.js +72 -0
- package/src/system/system-service.js +3 -0
- package/dist/web/assets/KnowledgeEditDialog-CZ5ziPrD.js +0 -1
- package/dist/web/assets/KnowledgePage-BRYC9S5Y.js +0 -1
- package/dist/web/assets/PersonalProjectsPage-DEh8XWby.js +0 -1
- package/dist/web/assets/ProjectAgentsPage-Crh7PrXa.css +0 -1
- package/dist/web/assets/ProjectAgentsPage-DPnaH-TR.js +0 -11
- package/dist/web/assets/SearchableSelect-D3HM8YF8.js +0 -1
- package/dist/web/assets/conversation-launcher-settings-DrJEotdf.js +0 -1
- package/dist/web/assets/index-BnH5mcZx.css +0 -1
- package/dist/web/assets/index-GqJf1xqg.js +0 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ApplicationError } from '../app/application-error.js';
|
|
2
|
+
import { activeAgentRequestSignal } from '../app/agent-request-context.js';
|
|
2
3
|
|
|
3
4
|
const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
|
4
5
|
const MAX_REQUEST_TIMEOUT_MS = 2_147_483_647;
|
|
@@ -102,6 +103,35 @@ export class GraphitiProviderClient {
|
|
|
102
103
|
`/v1/project-agents/${encodeURIComponent(agentId)}?${query}`
|
|
103
104
|
);
|
|
104
105
|
}
|
|
106
|
+
getProjectAgentMemory({ personalSpaceId, personalProjectId, agentId, limit = 1 }) {
|
|
107
|
+
const query = new URLSearchParams({
|
|
108
|
+
personal_space_id: personalSpaceId, personal_project_id: personalProjectId,
|
|
109
|
+
limit: String(limit)
|
|
110
|
+
});
|
|
111
|
+
return this.#request(`/v1/project-agents/${encodeURIComponent(agentId)}/memory?${query}`);
|
|
112
|
+
}
|
|
113
|
+
writeProjectAgentMemory(input) {
|
|
114
|
+
return this.#request(`/v1/project-agents/${encodeURIComponent(input.agent_id)}/memory`, {
|
|
115
|
+
method: 'PUT', body: input
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
resolveProjectAgentContext(input) {
|
|
119
|
+
return this.#request('/v1/project-agent-context/resolve', { method: 'POST', body: input });
|
|
120
|
+
}
|
|
121
|
+
beginTaskContext(input) {
|
|
122
|
+
return this.#request('/v1/task-contexts', { method: 'PUT', body: input });
|
|
123
|
+
}
|
|
124
|
+
getTaskContext(token, query) {
|
|
125
|
+
return this.#request(`/v1/task-contexts/${encodeURIComponent(token)}?${new URLSearchParams(query)}`);
|
|
126
|
+
}
|
|
127
|
+
checkpointTaskContext(token, input) {
|
|
128
|
+
return this.#request(`/v1/task-contexts/${encodeURIComponent(token)}/checkpoint`, {
|
|
129
|
+
method: 'PUT', body: input
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
verifyTaskCheckpoint(query) {
|
|
133
|
+
return this.#request(`/v1/task-context-sessions/checkpoint?${new URLSearchParams(query)}`);
|
|
134
|
+
}
|
|
105
135
|
deleteProjectAgent(personalSpaceId, agentId, reason = 'archived by user') {
|
|
106
136
|
const query = new URLSearchParams({
|
|
107
137
|
personal_space_id: personalSpaceId,
|
|
@@ -169,8 +199,12 @@ export class GraphitiProviderClient {
|
|
|
169
199
|
if (limit !== null && limit !== undefined) query.set('limit', String(limit));
|
|
170
200
|
return this.#request(`/v1/project-agent-tasks?${query}`);
|
|
171
201
|
}
|
|
172
|
-
viewProjectAgentTask(personalSpaceId, taskId, {
|
|
202
|
+
viewProjectAgentTask(personalSpaceId, taskId, {
|
|
203
|
+
personalProjectId = null,
|
|
204
|
+
includeEvents = true
|
|
205
|
+
} = {}) {
|
|
173
206
|
const query = new URLSearchParams({ personal_space_id: personalSpaceId });
|
|
207
|
+
if (personalProjectId) query.set('personal_project_id', personalProjectId);
|
|
174
208
|
query.set('include_events', String(includeEvents));
|
|
175
209
|
return this.#request(
|
|
176
210
|
`/v1/project-agent-tasks/${encodeURIComponent(taskId)}?${query}`
|
|
@@ -186,6 +220,7 @@ export class GraphitiProviderClient {
|
|
|
186
220
|
}
|
|
187
221
|
listProjectAgentActivity({
|
|
188
222
|
personalSpaceId,
|
|
223
|
+
personalProjectId = null,
|
|
189
224
|
agentId,
|
|
190
225
|
fromDate = null,
|
|
191
226
|
toDate = null
|
|
@@ -196,6 +231,7 @@ export class GraphitiProviderClient {
|
|
|
196
231
|
from: fromDate ?? toDate ?? today,
|
|
197
232
|
to: toDate ?? fromDate ?? today
|
|
198
233
|
});
|
|
234
|
+
if (personalProjectId) query.set('personal_project_id', personalProjectId);
|
|
199
235
|
return this.#request(
|
|
200
236
|
`/v1/project-agents/${encodeURIComponent(agentId)}/activity?${query}`
|
|
201
237
|
);
|
|
@@ -634,7 +670,10 @@ export class GraphitiProviderClient {
|
|
|
634
670
|
timedOut = true;
|
|
635
671
|
controller.abort(new Error('Graphiti provider request timed out'));
|
|
636
672
|
}, this.requestTimeoutMs);
|
|
637
|
-
const
|
|
673
|
+
const callerSignal = activeAgentRequestSignal();
|
|
674
|
+
const signal = callerSignal
|
|
675
|
+
? AbortSignal.any([controller.signal, callerSignal])
|
|
676
|
+
: controller.signal;
|
|
638
677
|
let response;
|
|
639
678
|
let payload;
|
|
640
679
|
try {
|
|
@@ -646,6 +685,10 @@ export class GraphitiProviderClient {
|
|
|
646
685
|
});
|
|
647
686
|
payload = await parseResponse(response);
|
|
648
687
|
} catch (error) {
|
|
688
|
+
if (error instanceof ProviderRequestError) throw error;
|
|
689
|
+
if (!timedOut && callerSignal?.aborted) {
|
|
690
|
+
throw callerSignal.reason ?? new DOMException('Aborted', 'AbortError');
|
|
691
|
+
}
|
|
649
692
|
const diagnostic = {
|
|
650
693
|
category: timedOut ? 'provider_timeout' : 'provider_unavailable',
|
|
651
694
|
status: timedOut ? 504 : 0,
|
|
@@ -684,9 +727,28 @@ export class GraphitiProviderClient {
|
|
|
684
727
|
|
|
685
728
|
async function parseResponse(response) {
|
|
686
729
|
const type = response.headers.get('content-type') ?? '';
|
|
687
|
-
if (type.includes('application/json')) return response.json();
|
|
688
730
|
const text = await response.text();
|
|
689
|
-
|
|
731
|
+
if (!type.includes('application/json')) {
|
|
732
|
+
if (!response.ok) return text ? { detail: text } : null;
|
|
733
|
+
throw invalidResponse(
|
|
734
|
+
text ? 'Graphiti provider returned a non-JSON response.'
|
|
735
|
+
: 'Graphiti provider returned an empty response.'
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
if (!text.trim()) {
|
|
739
|
+
if (!response.ok) return null;
|
|
740
|
+
throw invalidResponse('Graphiti provider returned an empty response.');
|
|
741
|
+
}
|
|
742
|
+
try {
|
|
743
|
+
return JSON.parse(text);
|
|
744
|
+
} catch {
|
|
745
|
+
if (!response.ok) return null;
|
|
746
|
+
throw invalidResponse('Graphiti provider returned invalid JSON.');
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function invalidResponse(message) {
|
|
751
|
+
return new ProviderRequestError(message, { code: 'provider_invalid_response' });
|
|
690
752
|
}
|
|
691
753
|
|
|
692
754
|
function providerErrorDiagnostic(status, payload) {
|
package/src/http/api-router.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readJson, sendJson } from './response.js';
|
|
2
2
|
import { handleExternalKnowledgeApiRequest } from './external-knowledge-api-router.js';
|
|
3
3
|
import { handleGraphApiRequest } from './graph-api-router.js';
|
|
4
|
+
import { handleEmployeeApiRequest } from './employee-api-router.js';
|
|
4
5
|
|
|
5
6
|
export async function handleApiRequest({
|
|
6
7
|
request,
|
|
@@ -36,13 +37,24 @@ export async function handleApiRequest({
|
|
|
36
37
|
return true;
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
if (system && url.pathname === '/api/system/version' && request.method === 'GET') {
|
|
41
|
+
sendJson(response, 200, await system.versionStatus());
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
39
45
|
if (system && url.pathname === '/api/system/runtime' && request.method === 'GET') {
|
|
40
46
|
sendJson(response, 200, system.runtimeStatus());
|
|
41
47
|
return true;
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
if (system && url.pathname === '/api/system/runtime/leases' && request.method === 'POST') {
|
|
45
|
-
|
|
51
|
+
const lease = await system.acquireRuntimeLease(await readJson(request));
|
|
52
|
+
if (response.destroyed) {
|
|
53
|
+
// Startup can outlive the caller's timeout. No recipient can now renew/release this handle.
|
|
54
|
+
if (lease.leaseId) system.releaseRuntimeLease(lease.leaseId);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
sendJson(response, 201, lease);
|
|
46
58
|
return true;
|
|
47
59
|
}
|
|
48
60
|
|
|
@@ -51,7 +63,7 @@ export async function handleApiRequest({
|
|
|
51
63
|
sendJson(
|
|
52
64
|
response,
|
|
53
65
|
200,
|
|
54
|
-
system.refreshRuntimeLease(
|
|
66
|
+
system.refreshRuntimeLease(decodeRuntimeLeaseId(runtimeLease[1]))
|
|
55
67
|
);
|
|
56
68
|
return true;
|
|
57
69
|
}
|
|
@@ -59,7 +71,7 @@ export async function handleApiRequest({
|
|
|
59
71
|
sendJson(
|
|
60
72
|
response,
|
|
61
73
|
200,
|
|
62
|
-
system.releaseRuntimeLease(
|
|
74
|
+
system.releaseRuntimeLease(decodeRuntimeLeaseId(runtimeLease[1]))
|
|
63
75
|
);
|
|
64
76
|
return true;
|
|
65
77
|
}
|
|
@@ -76,8 +88,12 @@ export async function handleApiRequest({
|
|
|
76
88
|
: await externalRequest();
|
|
77
89
|
if (handledExternal) return true;
|
|
78
90
|
|
|
79
|
-
const graphRequest = () =>
|
|
80
|
-
|
|
91
|
+
const graphRequest = async () =>
|
|
92
|
+
await handleEmployeeApiRequest({ request, response, url, app }) ||
|
|
93
|
+
await handleGraphApiRequest({ request, response, url, app });
|
|
94
|
+
if (system?.withGraphRuntimeLease && (
|
|
95
|
+
url.pathname.startsWith('/api/') || url.pathname.startsWith('/employee-workspaces/')
|
|
96
|
+
)) {
|
|
81
97
|
return system.withGraphRuntimeLease(
|
|
82
98
|
`http:${request.method}:${url.pathname}`,
|
|
83
99
|
graphRequest
|
|
@@ -86,6 +102,15 @@ export async function handleApiRequest({
|
|
|
86
102
|
return graphRequest();
|
|
87
103
|
}
|
|
88
104
|
|
|
105
|
+
function decodeRuntimeLeaseId(value) {
|
|
106
|
+
try {
|
|
107
|
+
return decodeURIComponent(value);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (error instanceof URIError) throw new TypeError('Runtime lease ID is invalid');
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
89
114
|
function externalRequestNeedsGraph(pathname, method) {
|
|
90
115
|
if (pathname === '/api/connected-knowledge/search' && method === 'POST') return true;
|
|
91
116
|
if (pathname === '/api/external-knowledge/bindings' && method === 'POST') return true;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { EmployeeError } from '../employees/manifest.js';
|
|
2
|
+
import { readJson, sendJson } from './response.js';
|
|
3
|
+
|
|
4
|
+
export async function handleEmployeeApiRequest({ request, response, url, app }) {
|
|
5
|
+
const employees = app.employees;
|
|
6
|
+
const apiPath = url.pathname.match(/^\/api\/employee-templates(?:\/([^/]+)\/(recruit|workspace|tools|call))?$/);
|
|
7
|
+
const workspacePath = url.pathname.match(/^\/employee-workspaces\/([^/]+)\/([^/]+)(\/.*)?$/);
|
|
8
|
+
if (!apiPath && !workspacePath) return false;
|
|
9
|
+
if (!employees) throw new EmployeeError('Employee runtime is unavailable', 503, 'runtime_unavailable');
|
|
10
|
+
if (workspacePath) {
|
|
11
|
+
const templateId = decodeEmployeePathSegment(workspacePath[1]);
|
|
12
|
+
const personalProjectId = decodeEmployeePathSegment(workspacePath[2]);
|
|
13
|
+
if (!workspacePath[3]) {
|
|
14
|
+
response.writeHead(308, { location: `${url.pathname}/${url.search}` });
|
|
15
|
+
response.end();
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
await employees.handleHttp(request, response, {
|
|
19
|
+
templateId,
|
|
20
|
+
personalProjectId,
|
|
21
|
+
relativePath: `${workspacePath[3]}${url.search}`,
|
|
22
|
+
origin: `http://${request.headers.host}`
|
|
23
|
+
});
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
const input = {
|
|
27
|
+
templateId: apiPath[1] ? decodeEmployeePathSegment(apiPath[1]) : undefined,
|
|
28
|
+
personalSpaceId: url.searchParams.get('personalSpaceId'),
|
|
29
|
+
personalProjectId: url.searchParams.get('personalProjectId')
|
|
30
|
+
};
|
|
31
|
+
let result;
|
|
32
|
+
if (!apiPath[1] && request.method === 'GET') result = await employees.list(input);
|
|
33
|
+
else if (apiPath[2] === 'recruit' && request.method === 'POST') {
|
|
34
|
+
const body = await readJson(request);
|
|
35
|
+
if (!body || typeof body !== 'object' || Array.isArray(body) ||
|
|
36
|
+
Object.keys(body).some((key) => !['personalSpaceId', 'personalProjectId', 'personalProjectIds', 'replaceAssignments', 'expectedAssignmentsVersion', 'reactivate', 'management'].includes(key)) ||
|
|
37
|
+
(body.reactivate !== undefined && typeof body.reactivate !== 'boolean')) {
|
|
38
|
+
throw new TypeError('Invalid employee recruitment request');
|
|
39
|
+
}
|
|
40
|
+
result = await employees.recruit({ ...body, templateId: input.templateId });
|
|
41
|
+
} else if (apiPath[2] === 'workspace' && request.method === 'GET') result = await employees.workspace(input);
|
|
42
|
+
else if (apiPath[2] === 'tools' && request.method === 'GET') result = await employees.describeTools(input);
|
|
43
|
+
else if (apiPath[2] === 'call' && request.method === 'POST') {
|
|
44
|
+
const body = await readJson(request);
|
|
45
|
+
if (!body || typeof body !== 'object' || Array.isArray(body) ||
|
|
46
|
+
Object.keys(body).some((key) => !['personalSpaceId', 'personalProjectId', 'tool', 'arguments'].includes(key)) ||
|
|
47
|
+
typeof body.tool !== 'string') throw new TypeError('Invalid employee tool call');
|
|
48
|
+
result = await employees.callTool({ ...body, templateId: input.templateId });
|
|
49
|
+
} else {
|
|
50
|
+
sendJson(response, 405, { error: 'Method not allowed' });
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
sendJson(response, 200, result);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function decodeEmployeePathSegment(value) {
|
|
58
|
+
try {
|
|
59
|
+
return decodeURIComponent(value);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (error instanceof URIError) throw new TypeError('Invalid employee path encoding');
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
} from '../app/application-error.js';
|
|
5
5
|
import { JsonBodyTooLargeError } from './response.js';
|
|
6
6
|
import { ProviderRequestError } from '../graphiti/provider-client.js';
|
|
7
|
+
import { EmployeeError } from '../employees/manifest.js';
|
|
7
8
|
|
|
8
9
|
const BAD_REQUEST_CODES = new Set([
|
|
9
10
|
ApplicationErrorCode.NOT_FOUND,
|
|
@@ -11,6 +12,9 @@ const BAD_REQUEST_CODES = new Set([
|
|
|
11
12
|
]);
|
|
12
13
|
|
|
13
14
|
export function mapHttpError(error) {
|
|
15
|
+
if (error instanceof EmployeeError) {
|
|
16
|
+
return { status: error.status, body: { error: error.message, code: error.code } };
|
|
17
|
+
}
|
|
14
18
|
if (error instanceof JsonBodyTooLargeError) {
|
|
15
19
|
return { status: 413, body: { error: 'Request body too large' } };
|
|
16
20
|
}
|
|
@@ -265,6 +265,7 @@ export async function handleGraphApiRequest({
|
|
|
265
265
|
if (projectAgentTaskPath && request.method === 'GET') {
|
|
266
266
|
sendJson(response, 200, await app.viewProjectAgentTask({
|
|
267
267
|
personalSpaceId: url.searchParams.get('personalSpaceId'),
|
|
268
|
+
personalProjectId: url.searchParams.get('personalProjectId'),
|
|
268
269
|
taskId: decodeURIComponent(projectAgentTaskPath[1]),
|
|
269
270
|
includeEvents: url.searchParams.get('includeEvents') !== 'false'
|
|
270
271
|
}));
|
|
@@ -273,6 +274,7 @@ export async function handleGraphApiRequest({
|
|
|
273
274
|
if (url.pathname === '/api/project-agent-activity' && request.method === 'GET') {
|
|
274
275
|
sendJson(response, 200, await app.viewProjectAgentActivity({
|
|
275
276
|
personalSpaceId: url.searchParams.get('personalSpaceId'),
|
|
277
|
+
personalProjectId: url.searchParams.get('personalProjectId'),
|
|
276
278
|
agentId: url.searchParams.get('agentId'),
|
|
277
279
|
fromDate: url.searchParams.get('fromDate'),
|
|
278
280
|
toDate: url.searchParams.get('toDate')
|
|
@@ -54,10 +54,13 @@ export function rejectRequestOutsidePolicy({
|
|
|
54
54
|
function hasLanAccess(authorization, accessToken) {
|
|
55
55
|
if (typeof accessToken !== 'string' || !accessToken) return false;
|
|
56
56
|
const expected = `Basic ${Buffer.from(`fuli:${accessToken}`).toString('base64')}`;
|
|
57
|
-
if (typeof authorization !== 'string'
|
|
57
|
+
if (typeof authorization !== 'string') {
|
|
58
58
|
return false;
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
const providedBytes = Buffer.from(authorization);
|
|
61
|
+
const expectedBytes = Buffer.from(expected);
|
|
62
|
+
return providedBytes.length === expectedBytes.length &&
|
|
63
|
+
timingSafeEqual(providedBytes, expectedBytes);
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
function isLoopbackAddress(address) {
|
|
@@ -67,7 +70,7 @@ function isLoopbackAddress(address) {
|
|
|
67
70
|
function requiresJson(request) {
|
|
68
71
|
if (!['PATCH', 'POST', 'PUT'].includes(request.method)) return false;
|
|
69
72
|
const pathname = new URL(request.url, 'http://127.0.0.1').pathname;
|
|
70
|
-
return pathname.startsWith('/api/');
|
|
73
|
+
return pathname.startsWith('/api/') || pathname.startsWith('/employee-workspaces/');
|
|
71
74
|
}
|
|
72
75
|
|
|
73
76
|
function isJson(contentType) {
|
|
@@ -38,7 +38,7 @@ export function serveStatic(pathname, response) {
|
|
|
38
38
|
const pathFromRoot = relative(WEB_ROOT, requestedFile);
|
|
39
39
|
if (pathFromRoot === '..' || pathFromRoot.startsWith(`..${sep}`) ||
|
|
40
40
|
isAbsolute(pathFromRoot)) {
|
|
41
|
-
response.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });
|
|
41
|
+
response.writeHead(404, { 'content-type': 'text/plain; charset=utf-8', 'cache-control': 'no-store' });
|
|
42
42
|
response.end('Not found');
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
@@ -49,13 +49,14 @@ export function serveStatic(pathname, response) {
|
|
|
49
49
|
? join(WEB_ROOT, 'index.html')
|
|
50
50
|
: null;
|
|
51
51
|
if (!filePath || !existsSync(filePath)) {
|
|
52
|
-
response.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });
|
|
52
|
+
response.writeHead(404, { 'content-type': 'text/plain; charset=utf-8', 'cache-control': 'no-store' });
|
|
53
53
|
response.end('Not found');
|
|
54
54
|
return;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
response.writeHead(200, {
|
|
58
|
-
'content-type': MIME_TYPES[extname(filePath)] ?? 'application/octet-stream'
|
|
58
|
+
'content-type': MIME_TYPES[extname(filePath)] ?? 'application/octet-stream',
|
|
59
|
+
...(extname(filePath) === '.html' ? { 'cache-control': 'no-cache' } : {})
|
|
59
60
|
});
|
|
60
61
|
response.end(readFileSync(filePath));
|
|
61
62
|
}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
CallToolRequestSchema,
|
|
4
|
+
ListToolsRequestSchema
|
|
5
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
3
6
|
|
|
4
7
|
import { callAgentTool, listAgentTools } from '../agent-tools.js';
|
|
8
|
+
import { ApplicationError, ApplicationErrorCode } from '../app/application-error.js';
|
|
5
9
|
import { FULI_VERSION } from '../package-metadata.js';
|
|
6
10
|
import { MCP_INSTRUCTIONS } from './instructions.js';
|
|
7
11
|
import { createProjectActionPreviewTokens } from './project-action-preview-tokens.js';
|
|
@@ -24,8 +28,13 @@ import {
|
|
|
24
28
|
} from './session-id.js';
|
|
25
29
|
|
|
26
30
|
const TOOL_RESULT_LIMIT_BYTES = Object.freeze({
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
list_employee_templates: 32 * 1024,
|
|
32
|
+
recruit_employee: 64 * 1024,
|
|
33
|
+
list_employee_tools: 64 * 1024,
|
|
34
|
+
call_employee_tool: 64 * 1024,
|
|
35
|
+
begin_task_context: 64 * 1024,
|
|
36
|
+
checkpoint_task_knowledge: 16 * 1024,
|
|
37
|
+
get_collaboration_preferences: 64 * 1024,
|
|
29
38
|
get_user_taste_skill: 32 * 1024,
|
|
30
39
|
search_knowledge_graph: 32 * 1024,
|
|
31
40
|
search_connected_knowledge: 64 * 1024,
|
|
@@ -38,6 +47,8 @@ const TOOL_RESULT_LIMIT_BYTES = Object.freeze({
|
|
|
38
47
|
get_project_agent: 32 * 1024,
|
|
39
48
|
list_project_agent_assignments: 32 * 1024,
|
|
40
49
|
get_project_agent_context: 64 * 1024,
|
|
50
|
+
get_project_agent_memory: 128 * 1024,
|
|
51
|
+
checkpoint_project_agent_memory: 64 * 1024,
|
|
41
52
|
coordinate_project_agent_task: 256 * 1024,
|
|
42
53
|
acquire_runtime_lease: 4 * 1024,
|
|
43
54
|
refresh_runtime_lease: 4 * 1024,
|
|
@@ -63,33 +74,46 @@ export function createMcpServer(
|
|
|
63
74
|
env = process.env,
|
|
64
75
|
clock = () => new Date(),
|
|
65
76
|
sourceApplication = nativeCodexThreadId(env) ? 'codex' : 'other',
|
|
66
|
-
withRuntimeLease = (_owner, operation) => operation()
|
|
77
|
+
withRuntimeLease = (_owner, operation) => operation(),
|
|
78
|
+
hostSessionId = null,
|
|
79
|
+
instructions = MCP_INSTRUCTIONS,
|
|
80
|
+
toolNames = null,
|
|
81
|
+
prepareToolInput = (_name, input) => input,
|
|
82
|
+
registerResources = true
|
|
67
83
|
} = {}
|
|
68
84
|
) {
|
|
69
85
|
const server = new McpServer(
|
|
70
86
|
{ name: 'fuli', version: FULI_VERSION },
|
|
71
|
-
{ instructions
|
|
87
|
+
{ instructions }
|
|
72
88
|
);
|
|
73
89
|
|
|
74
|
-
const nativeThreadId = nativeCodexThreadId(env);
|
|
75
90
|
const authoritativeSourceApplication = normalizeMcpSourceApplication(
|
|
76
91
|
sourceApplication
|
|
77
92
|
);
|
|
93
|
+
const nativeThreadId = authoritativeSourceApplication === 'codex' ? nativeCodexThreadId(env) : null;
|
|
78
94
|
const tools = createToolMap(
|
|
79
95
|
app,
|
|
80
96
|
createProjectActionPreviewTokens(),
|
|
81
97
|
nativeThreadId,
|
|
82
|
-
mcpHostSessionId(env),
|
|
98
|
+
hostSessionId ?? mcpHostSessionId(authoritativeSourceApplication === 'codex' ? env : {}),
|
|
83
99
|
clock,
|
|
84
100
|
authoritativeSourceApplication,
|
|
85
|
-
withRuntimeLease
|
|
101
|
+
withRuntimeLease,
|
|
102
|
+
toolNames,
|
|
103
|
+
prepareToolInput
|
|
86
104
|
);
|
|
87
105
|
for (const tool of tools.values()) registerTool(server, tool);
|
|
88
|
-
|
|
106
|
+
if (!tools.size) registerEmptyToolList(server);
|
|
107
|
+
if (registerResources) registerFuliContextResources(server, app, { withRuntimeLease });
|
|
89
108
|
registerCallHandler(server, tools);
|
|
90
109
|
return server;
|
|
91
110
|
}
|
|
92
111
|
|
|
112
|
+
function registerEmptyToolList(server) {
|
|
113
|
+
server.server.registerCapabilities({ tools: { listChanged: true } });
|
|
114
|
+
server.server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: [] }));
|
|
115
|
+
}
|
|
116
|
+
|
|
93
117
|
function createToolMap(
|
|
94
118
|
app,
|
|
95
119
|
projectActionPreviews,
|
|
@@ -97,29 +121,52 @@ function createToolMap(
|
|
|
97
121
|
hostSessionId,
|
|
98
122
|
clock,
|
|
99
123
|
sourceApplication,
|
|
100
|
-
withRuntimeLease
|
|
124
|
+
withRuntimeLease,
|
|
125
|
+
toolNames,
|
|
126
|
+
prepareToolInput
|
|
101
127
|
) {
|
|
102
128
|
const commonKnowledgePreviews = createCommonKnowledgePreviewTokens();
|
|
103
|
-
|
|
129
|
+
if (toolNames !== null && !Array.isArray(toolNames)) {
|
|
130
|
+
throw new TypeError('MCP toolNames must be an array or null');
|
|
131
|
+
}
|
|
132
|
+
const definitions = listAgentTools();
|
|
133
|
+
const allowedTools = toolNames === null ? null : new Set(toolNames);
|
|
134
|
+
if (allowedTools) {
|
|
135
|
+
const available = new Set(definitions.map(({ name }) => name));
|
|
136
|
+
const unknown = [...allowedTools].filter((name) => !available.has(name));
|
|
137
|
+
if (unknown.length) {
|
|
138
|
+
throw new TypeError(
|
|
139
|
+
`Unknown MCP tool name${unknown.length === 1 ? '' : 's'}: ${unknown.join(', ')}`
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return new Map(definitions
|
|
144
|
+
.filter((definition) => !allowedTools || allowedTools.has(definition.name))
|
|
145
|
+
.map((definition) => [definition.name, {
|
|
104
146
|
definition,
|
|
105
147
|
schema: jsonSchemaToZod(definition.inputSchema),
|
|
106
|
-
invoke: (input) =>
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
148
|
+
invoke: (input, requestContext = null) => {
|
|
149
|
+
requestContext?.signal?.throwIfAborted?.();
|
|
150
|
+
return withRuntimeLease(
|
|
151
|
+
`mcp-tool:${definition.name}`,
|
|
152
|
+
() => invokeAgentTool(
|
|
153
|
+
app,
|
|
112
154
|
definition.name,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
155
|
+
normalizeAgentSessionInput(
|
|
156
|
+
definition.name,
|
|
157
|
+
prepareToolInput(definition.name, input),
|
|
158
|
+
nativeThreadId,
|
|
159
|
+
hostSessionId,
|
|
160
|
+
clock,
|
|
161
|
+
sourceApplication
|
|
162
|
+
),
|
|
163
|
+
projectActionPreviews,
|
|
164
|
+
commonKnowledgePreviews,
|
|
165
|
+
requestContext
|
|
118
166
|
),
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
)
|
|
167
|
+
requestContext
|
|
168
|
+
);
|
|
169
|
+
}
|
|
123
170
|
}]));
|
|
124
171
|
}
|
|
125
172
|
|
|
@@ -131,22 +178,35 @@ function registerTool(server, tool) {
|
|
|
131
178
|
inputSchema: schema,
|
|
132
179
|
outputSchema: openObjectSchema(),
|
|
133
180
|
annotations: annotationsFor(definition.name)
|
|
134
|
-
}, (input) => invokeTool(tool, input));
|
|
181
|
+
}, (input, extra) => invokeTool(tool, input, extra));
|
|
135
182
|
}
|
|
136
183
|
|
|
137
184
|
function registerCallHandler(server, tools) {
|
|
138
|
-
server.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
185
|
+
server.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
139
186
|
const tool = tools.get(request.params.name);
|
|
140
|
-
if (!tool)
|
|
187
|
+
if (!tool) {
|
|
188
|
+
return errorToolResult(new ApplicationError(
|
|
189
|
+
ApplicationErrorCode.NOT_FOUND,
|
|
190
|
+
'Unknown Fuli tool'
|
|
191
|
+
));
|
|
192
|
+
}
|
|
141
193
|
const parsed = await tool.schema.safeParseAsync(request.params.arguments ?? {});
|
|
142
|
-
if (!parsed.success)
|
|
143
|
-
|
|
194
|
+
if (!parsed.success) {
|
|
195
|
+
return protocolErrorResult(
|
|
196
|
+
'Input validation error',
|
|
197
|
+
parsed.error.issues.map(({ path, message }) => ({
|
|
198
|
+
field: path.length ? path.join('.') : 'arguments',
|
|
199
|
+
message
|
|
200
|
+
}))
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
return invokeTool(tool, parsed.data, extra);
|
|
144
204
|
});
|
|
145
205
|
}
|
|
146
206
|
|
|
147
|
-
async function invokeTool(tool, input) {
|
|
207
|
+
async function invokeTool(tool, input, requestContext = null) {
|
|
148
208
|
try {
|
|
149
|
-
const value = await tool.invoke(input);
|
|
209
|
+
const value = await tool.invoke(input, requestContext);
|
|
150
210
|
auditLifecycleTool(tool.definition.name);
|
|
151
211
|
const limitBytes = TOOL_RESULT_LIMIT_BYTES[tool.definition.name];
|
|
152
212
|
if (tool.definition.name === 'begin_task_context') {
|
|
@@ -167,10 +227,11 @@ async function invokeAgentTool(
|
|
|
167
227
|
name,
|
|
168
228
|
input,
|
|
169
229
|
projectActionPreviews,
|
|
170
|
-
commonKnowledgePreviews
|
|
230
|
+
commonKnowledgePreviews,
|
|
231
|
+
requestContext = null
|
|
171
232
|
) {
|
|
172
233
|
if (name === 'preview_personal_project_action') {
|
|
173
|
-
const result = await callAgentTool(app, name, input);
|
|
234
|
+
const result = await callAgentTool(app, name, input, requestContext);
|
|
174
235
|
return {
|
|
175
236
|
...result,
|
|
176
237
|
previewToken: projectActionPreviews.issue(input)
|
|
@@ -180,7 +241,7 @@ async function invokeAgentTool(
|
|
|
180
241
|
projectActionPreviews.consume(input.previewToken, input);
|
|
181
242
|
}
|
|
182
243
|
if (name === 'preview_common_knowledge_promotion') {
|
|
183
|
-
const result = await callAgentTool(app, name, input);
|
|
244
|
+
const result = await callAgentTool(app, name, input, requestContext);
|
|
184
245
|
return {
|
|
185
246
|
...result,
|
|
186
247
|
previewToken: commonKnowledgePreviews.issue(input)
|
|
@@ -189,5 +250,5 @@ async function invokeAgentTool(
|
|
|
189
250
|
if (name === 'apply_common_knowledge_promotion') {
|
|
190
251
|
commonKnowledgePreviews.consume(input.previewToken, input);
|
|
191
252
|
}
|
|
192
|
-
return callAgentTool(app, name, input);
|
|
253
|
+
return callAgentTool(app, name, input, requestContext);
|
|
193
254
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { listAgentTools } from '../agent-tools.js';
|
|
2
|
+
import {
|
|
3
|
+
mcpHostSessionId,
|
|
4
|
+
nativeCodexThreadId,
|
|
5
|
+
normalizeAgentSessionInput,
|
|
6
|
+
normalizeMcpSourceApplication
|
|
7
|
+
} from './session-id.js';
|
|
8
|
+
import { jsonSchemaToZod } from './tool-schema.js';
|
|
9
|
+
|
|
10
|
+
export async function prepareDirectAgentToolCall({
|
|
11
|
+
toolName,
|
|
12
|
+
input,
|
|
13
|
+
sourceApplication = 'other',
|
|
14
|
+
env = process.env,
|
|
15
|
+
clock = () => new Date(),
|
|
16
|
+
hostSessionId = null
|
|
17
|
+
}) {
|
|
18
|
+
const definition = listAgentTools().find((item) => item.name === toolName);
|
|
19
|
+
if (!definition) throw new TypeError('Unknown tool');
|
|
20
|
+
const parsed = await jsonSchemaToZod(definition.inputSchema).safeParseAsync(input ?? {});
|
|
21
|
+
if (!parsed.success) throw new TypeError('Input validation error');
|
|
22
|
+
const source = normalizeMcpSourceApplication(sourceApplication);
|
|
23
|
+
const nativeThreadId = source === 'codex' ? nativeCodexThreadId(env) : null;
|
|
24
|
+
const authoritativeHostSessionId = hostSessionId ?? mcpHostSessionId(
|
|
25
|
+
source === 'codex' ? env : {}
|
|
26
|
+
);
|
|
27
|
+
return {
|
|
28
|
+
definition,
|
|
29
|
+
input: normalizeAgentSessionInput(
|
|
30
|
+
toolName,
|
|
31
|
+
parsed.data,
|
|
32
|
+
nativeThreadId,
|
|
33
|
+
authoritativeHostSessionId,
|
|
34
|
+
clock,
|
|
35
|
+
source
|
|
36
|
+
)
|
|
37
|
+
};
|
|
38
|
+
}
|
package/src/mcp/instructions.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export const MCP_INSTRUCTIONS = [
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
"Each user task: begin_task_context hook-provided task context; else before tools/answer call exactly get_collaboration_preferences(projectPath=cwd taskPrompt=current user request); never substitute project action. All effective_preferences: personal-global everywhere; matched project, authorized inheritable parent. get_user_taste_skill never replaces user Skill. Resolve deferred_conflict; never guess personalProjectId. Writes=actual payload; final text=not compliance.",
|
|
3
|
+
"Before asking stable project fact/method: task_knowledge_recall; miss: search_current_project_knowledge with focused action/artifact/target/ID queries, never only the full request. Active child first, then inheritable parent; no local_only/RELATED_TO.",
|
|
4
|
+
"Before saying unknown: search_knowledge_graph; extra projects=exact IDs. all_local_confirmed only after consent; rg only current repo/workspace. monitoring/Git MCP; search_connected_knowledge; agent_decide response-only.",
|
|
5
|
+
"record_decision_trace; errors: record_knowledge_feedback. Batch durable confirmed knowledge: capture_session_knowledge; no secrets. checkpoint_task_knowledge: capture_candidates/retain_nothing; record_knowledge_usage.",
|
|
6
|
+
"coordinate_project_agent_task=isolated context; Fuli never spawns. acquire_runtime_lease; run/report; release_runtime_lease in finally.",
|
|
7
|
+
"project_management_context: actionable work via authorized manager+board; keep chosen specialist; no excluded projects/extra model. Rename: exact current native session, host rename tool, manual-title protection; receipt after actual client result.",
|
|
8
|
+
"Task runs until all workerStatus terminal. executionSummary: one table row/worker: occupation emoji/label, actual executor/sourceApplication, work/status/session/link, source-labelled cumulative tokens. Missing=unknown; no invent/estimate/copy totals. Empty: omit. Configured/allowed/available not evidence.",
|
|
9
|
+
"Terminal-safe Markdown; no HTML. Match=candidate only. If supported: MUST begin sourceMarker.leadMarkdown; append sourceMarker.markdown; otherwise noMatchSourceMarker (empty). Keep unchanged."
|
|
9
10
|
].join(' ');
|