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,5 +1,8 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
|
|
3
|
+
import { ApplicationError, ApplicationErrorCode } from '../app/application-error.js';
|
|
4
|
+
import { agentMemoryRecord, prepareProjectAgentMemory } from './project-agent-memory.js';
|
|
5
|
+
|
|
3
6
|
import { onlineSourceUri } from './source-uri.js';
|
|
4
7
|
import {
|
|
5
8
|
relatedProjectGuidance,
|
|
@@ -8,28 +11,58 @@ import {
|
|
|
8
11
|
|
|
9
12
|
export async function beginTaskContext(application, {
|
|
10
13
|
sessionId,
|
|
14
|
+
turnId = null,
|
|
11
15
|
projectPath,
|
|
12
|
-
|
|
16
|
+
personalProjectId = null,
|
|
17
|
+
taskPrompt = null,
|
|
18
|
+
sourceApplication = 'other',
|
|
19
|
+
sourceSessionId = null,
|
|
20
|
+
projectAgentId = null,
|
|
21
|
+
workKind = null,
|
|
22
|
+
requiredCapabilities = []
|
|
13
23
|
}) {
|
|
24
|
+
const continuationToken = /^FULI_CHECKPOINT_REQUIRED: (fuli-task-[a-zA-Z0-9-]{8,128})\b/.exec(taskPrompt ?? '')?.[1];
|
|
25
|
+
const resumed = continuationToken
|
|
26
|
+
? await application.taskContextRegistry.context(continuationToken, sourceApplication) : null;
|
|
27
|
+
if (resumed && resumed.sessionId !== sessionId) {
|
|
28
|
+
throw validationError('Checkpoint continuation belongs to another host session');
|
|
29
|
+
}
|
|
14
30
|
const preferences = await application.getCollaborationPreferences({
|
|
31
|
+
personalProjectId,
|
|
15
32
|
projectPath,
|
|
33
|
+
sessionId,
|
|
34
|
+
turnId,
|
|
16
35
|
taskPrompt,
|
|
36
|
+
projectAgentId: resumed?.projectAgentId ?? projectAgentId,
|
|
37
|
+
sourceApplication,
|
|
38
|
+
sourceSessionId,
|
|
39
|
+
workKind,
|
|
40
|
+
requiredCapabilities,
|
|
17
41
|
agentInvocation: true,
|
|
18
42
|
agentToolName: 'begin_task_context'
|
|
19
43
|
});
|
|
20
|
-
|
|
44
|
+
if (resumed && resumed.personalProjectId !== preferences.context.personal_project_id) {
|
|
45
|
+
throw validationError('Checkpoint continuation belongs to another project');
|
|
46
|
+
}
|
|
47
|
+
const task = resumed ?? await application.taskContextRegistry.begin({
|
|
21
48
|
sessionId,
|
|
22
|
-
|
|
49
|
+
turnId,
|
|
50
|
+
personalProjectId: preferences.context.personal_project_id,
|
|
51
|
+
projectAgentId: preferences.context.project_agent_id,
|
|
52
|
+
sourceApplication,
|
|
53
|
+
sourceSessionId,
|
|
54
|
+
memoryRevision: preferences.project_agent_context?.memory?.revision ?? null
|
|
23
55
|
});
|
|
24
56
|
return {
|
|
25
57
|
taskContextToken: task.token,
|
|
26
58
|
task_context_token: task.token,
|
|
27
|
-
checkpoint_required:
|
|
59
|
+
checkpoint_required: task.checkpoint?.phase !== 'complete',
|
|
60
|
+
resumed_checkpoint: Boolean(resumed),
|
|
28
61
|
previous_checkpoint_missing: task.previousCheckpointMissing,
|
|
29
62
|
...preferences,
|
|
30
63
|
task_guidance: {
|
|
31
64
|
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.',
|
|
32
|
-
checkpoint: 'Before finishing, call checkpoint_task_knowledge with capture_candidates or retain_nothing.'
|
|
65
|
+
checkpoint: 'Before finishing, call checkpoint_task_knowledge with capture_candidates or retain_nothing. When durable role context changed, include agentMemory with the loaded revision and a bounded merged summary, decisions, open threads and next actions. Do not overwrite from truncated context or store raw transcripts.'
|
|
33
66
|
}
|
|
34
67
|
};
|
|
35
68
|
}
|
|
@@ -38,43 +71,120 @@ export async function checkpointTaskKnowledge(application, {
|
|
|
38
71
|
taskContextToken,
|
|
39
72
|
disposition,
|
|
40
73
|
reason,
|
|
41
|
-
capture = null
|
|
74
|
+
capture = null,
|
|
75
|
+
agentMemory = null,
|
|
76
|
+
sourceApplication = 'other',
|
|
77
|
+
personalProjectId = null,
|
|
78
|
+
remoteSessionId = null
|
|
42
79
|
}) {
|
|
43
80
|
if (!['capture_candidates', 'retain_nothing'].includes(disposition)) {
|
|
44
|
-
throw
|
|
81
|
+
throw validationError('Unknown task knowledge checkpoint disposition');
|
|
82
|
+
}
|
|
83
|
+
const task = await application.taskContextRegistry.context(taskContextToken, sourceApplication);
|
|
84
|
+
if (personalProjectId && task.personalProjectId !== personalProjectId) {
|
|
85
|
+
throw validationError('Task checkpoint belongs to another project');
|
|
86
|
+
}
|
|
87
|
+
if (remoteSessionId && task.sessionId !== remoteSessionId) {
|
|
88
|
+
throw validationError('Task checkpoint belongs to another remote session');
|
|
45
89
|
}
|
|
46
|
-
const task = application.taskContextRegistry.context(taskContextToken);
|
|
47
|
-
|
|
48
|
-
let captureResult = null;
|
|
49
90
|
if (disposition === 'capture_candidates') {
|
|
50
91
|
if (!capture) {
|
|
51
|
-
throw
|
|
92
|
+
throw validationError('capture_candidates requires a bounded capture payload');
|
|
93
|
+
}
|
|
94
|
+
} else if (capture) {
|
|
95
|
+
throw validationError('retain_nothing cannot include a capture payload');
|
|
96
|
+
}
|
|
97
|
+
const fingerprint = createHash('sha256').update(JSON.stringify(
|
|
98
|
+
canonicalCheckpoint({ disposition, reason, capture, agentMemory })
|
|
99
|
+
)).digest('hex');
|
|
100
|
+
if (task.checkpoint && task.checkpoint.fingerprint !== fingerprint) {
|
|
101
|
+
throw validationError(
|
|
102
|
+
'Task checkpoint already has different input; resume the original review or begin a new task context'
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
if (task.checkpoint?.phase === 'complete') {
|
|
106
|
+
return { status: 'checkpointed', disposition, reason,
|
|
107
|
+
personal_project_id: task.personalProjectId, project_agent_id: task.projectAgentId,
|
|
108
|
+
replayed: true, capture_status: task.checkpoint.captureStatus };
|
|
109
|
+
}
|
|
110
|
+
const captureInput = capture ? {
|
|
111
|
+
...capture,
|
|
112
|
+
targetKind: 'personal',
|
|
113
|
+
spaceId: application.config.personal.spaceId,
|
|
114
|
+
personalProjectId: task.personalProjectId,
|
|
115
|
+
projectAgentId: task.projectAgentId ?? null,
|
|
116
|
+
sessionId: task.sessionId,
|
|
117
|
+
sourceApplication,
|
|
118
|
+
idempotencyKey: `${task.token}:knowledge`
|
|
119
|
+
} : null;
|
|
120
|
+
// Local validation has no side effects. Reject correctable input before
|
|
121
|
+
// binding an immutable checkpoint or advancing the Agent's memory revision.
|
|
122
|
+
if (captureInput) application.validateCaptureSessionKnowledge(captureInput);
|
|
123
|
+
let memoryResult = null;
|
|
124
|
+
let memoryRequest = null;
|
|
125
|
+
if (agentMemory) {
|
|
126
|
+
if (!task.projectAgentId || !task.personalProjectId) {
|
|
127
|
+
throw validationError('Task has no durable project Agent; do not guess a memory target');
|
|
52
128
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
spaceId: application.config.personal.spaceId,
|
|
129
|
+
const prepared = await prepareProjectAgentMemory(application, {
|
|
130
|
+
personalSpaceId: application.config.personal.spaceId,
|
|
56
131
|
personalProjectId: task.personalProjectId,
|
|
57
|
-
|
|
58
|
-
|
|
132
|
+
agentId: task.projectAgentId,
|
|
133
|
+
expectedRevision: agentMemory.expectedRevision,
|
|
134
|
+
idempotencyKey: `${task.token}:memory`,
|
|
135
|
+
memory: agentMemory.memory, sourceApplication,
|
|
136
|
+
// The logical client session survives independent lifecycle-hook MCP
|
|
137
|
+
// processes; their host session ids do not. The Provider persists this
|
|
138
|
+
// stable task session as the memory checkpoint provenance.
|
|
139
|
+
sourceSessionId: task.sessionId
|
|
59
140
|
});
|
|
60
|
-
|
|
61
|
-
|
|
141
|
+
if (prepared.status === 'capture_disabled') memoryResult = prepared;
|
|
142
|
+
else memoryRequest = { expected_revision: prepared.request.expected_revision,
|
|
143
|
+
memory: prepared.request.memory };
|
|
62
144
|
}
|
|
63
|
-
|
|
64
|
-
application.taskContextRegistry.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
145
|
+
const checkpoint = { disposition, reason, fingerprint, captureStatus: null };
|
|
146
|
+
const preparedTask = await application.taskContextRegistry.prepare(
|
|
147
|
+
taskContextToken, checkpoint, sourceApplication, memoryRequest
|
|
148
|
+
);
|
|
149
|
+
if (memoryRequest) {
|
|
150
|
+
if (!preparedTask.agentMemory) {
|
|
151
|
+
throw new TypeError('Provider did not confirm atomic Agent memory preparation');
|
|
152
|
+
}
|
|
153
|
+
memoryResult = { status: 'checkpointed', ...agentMemoryRecord(preparedTask.agentMemory) };
|
|
154
|
+
}
|
|
155
|
+
const captureResult = captureInput
|
|
156
|
+
? await application.captureSessionKnowledge(captureInput) : null;
|
|
157
|
+
await application.taskContextRegistry.checkpoint(taskContextToken, {
|
|
158
|
+
...checkpoint, captureStatus: captureResult?.status ?? null
|
|
159
|
+
}, sourceApplication);
|
|
69
160
|
return {
|
|
70
161
|
status: 'checkpointed',
|
|
71
162
|
disposition,
|
|
72
163
|
reason,
|
|
73
164
|
personal_project_id: task.personalProjectId,
|
|
74
|
-
|
|
165
|
+
project_agent_id: task.projectAgentId ?? null,
|
|
166
|
+
capture: captureResult,
|
|
167
|
+
agent_memory: memoryResult ? {
|
|
168
|
+
status: memoryResult.status, agentId: memoryResult.agentId,
|
|
169
|
+
checkpointId: memoryResult.checkpointId, revision: memoryResult.revision,
|
|
170
|
+
sourceApplication: memoryResult.sourceApplication,
|
|
171
|
+
sourceSessionId: memoryResult.sourceSessionId, createdAt: memoryResult.createdAt
|
|
172
|
+
} : null
|
|
75
173
|
};
|
|
76
174
|
}
|
|
77
175
|
|
|
176
|
+
function canonicalCheckpoint(value) {
|
|
177
|
+
if (Array.isArray(value)) return value.map(canonicalCheckpoint);
|
|
178
|
+
if (!value || typeof value !== 'object') return value;
|
|
179
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [
|
|
180
|
+
key, canonicalCheckpoint(value[key])
|
|
181
|
+
]));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function validationError(message) {
|
|
185
|
+
return new ApplicationError(ApplicationErrorCode.VALIDATION, message);
|
|
186
|
+
}
|
|
187
|
+
|
|
78
188
|
export async function recordDecisionTrace(application, input) {
|
|
79
189
|
const trace = decisionTraceKnowledge(input);
|
|
80
190
|
return application.captureSessionKnowledge({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { dirname } from 'node:path';
|
|
2
|
+
import { createEmployeeService } from '../employees/service.js';
|
|
2
3
|
|
|
3
4
|
import { GraphitiProviderClient } from './provider-client.js';
|
|
4
5
|
import {
|
|
@@ -30,7 +31,7 @@ import {
|
|
|
30
31
|
} from './source-marker.js';
|
|
31
32
|
import { resolvePersonalProjectPath } from './project-path-context.js';
|
|
32
33
|
import { mergeExternalKnowledgeProjection } from '../external-knowledge/graph-projection.js';
|
|
33
|
-
import {
|
|
34
|
+
import { ProviderTaskContextRegistry } from '../mcp/provider-task-context-registry.js';
|
|
34
35
|
import { attachExternalKnowledgeRuntime } from '../external-knowledge/runtime.js';
|
|
35
36
|
import { resolveSetupPaths } from '../setup/paths.js';
|
|
36
37
|
import {
|
|
@@ -79,6 +80,7 @@ import {
|
|
|
79
80
|
getCollaborationPreferences as getCollaborationPreferencesWorkflow
|
|
80
81
|
} from './collaboration-preference-workflow.js';
|
|
81
82
|
import { buildUserTasteSkill } from './user-taste-skill.js';
|
|
83
|
+
import { resolveTaskEntryAgent, loadProjectAgentContinuity } from './project-agent-task-entry.js';
|
|
82
84
|
import { getWritingTasteProfile as getWritingTasteProfileWorkflow } from './writing-taste-profile-workflow.js';
|
|
83
85
|
import {
|
|
84
86
|
groupSubscriptions,
|
|
@@ -114,6 +116,7 @@ export function openFederatedGraphApplication({
|
|
|
114
116
|
env,
|
|
115
117
|
fetchImpl
|
|
116
118
|
});
|
|
119
|
+
app.employees = createEmployeeService({ app, runtimeConfigPath });
|
|
117
120
|
}
|
|
118
121
|
return app;
|
|
119
122
|
}
|
|
@@ -137,7 +140,7 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
137
140
|
agentAccessPolicyStore = new AgentAccessPolicyStore(),
|
|
138
141
|
consoleUrl = sourceConsoleUrl(null),
|
|
139
142
|
projectPathResolver = resolvePersonalProjectPath,
|
|
140
|
-
taskContextRegistry =
|
|
143
|
+
taskContextRegistry = null,
|
|
141
144
|
providerRequestTimeoutMs = undefined
|
|
142
145
|
} = {}) {
|
|
143
146
|
super();
|
|
@@ -147,7 +150,6 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
147
150
|
this.agentAccessPolicyStore = agentAccessPolicyStore;
|
|
148
151
|
this.consoleUrl = consoleUrl;
|
|
149
152
|
this.projectPathResolver = projectPathResolver;
|
|
150
|
-
this.taskContextRegistry = taskContextRegistry;
|
|
151
153
|
this.personal = new GraphitiProviderClient({
|
|
152
154
|
baseUrl: config.personal.providerUrl,
|
|
153
155
|
accessToken: config.personal.accessToken,
|
|
@@ -155,6 +157,9 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
155
157
|
fetchImpl,
|
|
156
158
|
requestTimeoutMs: providerRequestTimeoutMs
|
|
157
159
|
});
|
|
160
|
+
this.taskContextRegistry = taskContextRegistry ?? new ProviderTaskContextRegistry(
|
|
161
|
+
this.personal, config.personal.spaceId
|
|
162
|
+
);
|
|
158
163
|
this.workspaces = new Map(config.workspaces.map((workspace) => {
|
|
159
164
|
const provider = createWorkspaceProvider(workspace, {
|
|
160
165
|
fetchImpl, requestTimeoutMs: providerRequestTimeoutMs
|
|
@@ -171,8 +176,8 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
171
176
|
return checkpointTaskKnowledgeWorkflow(this, input);
|
|
172
177
|
}
|
|
173
178
|
|
|
174
|
-
verifyTaskCheckpoint({ sessionId }) {
|
|
175
|
-
return this.taskContextRegistry.verify(sessionId);
|
|
179
|
+
verifyTaskCheckpoint({ sessionId, sourceApplication = 'other' }) {
|
|
180
|
+
return this.taskContextRegistry.verify(sessionId, sourceApplication);
|
|
176
181
|
}
|
|
177
182
|
|
|
178
183
|
async recordDecisionTrace(input) {
|
|
@@ -180,6 +185,10 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
180
185
|
return recordDecisionTraceWorkflow(this, input);
|
|
181
186
|
}
|
|
182
187
|
|
|
188
|
+
validateCaptureSessionKnowledge(input) {
|
|
189
|
+
if (this.getCapturePolicy().enabled) this.#prepareKnowledgeCapture(input);
|
|
190
|
+
}
|
|
191
|
+
|
|
183
192
|
async captureSessionKnowledge(input) {
|
|
184
193
|
const capturePolicy = this.getCapturePolicy();
|
|
185
194
|
if (!capturePolicy.enabled) {
|
|
@@ -189,15 +198,8 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
189
198
|
capturePolicy
|
|
190
199
|
};
|
|
191
200
|
}
|
|
192
|
-
|
|
193
|
-
const episode = providerEpisode(input);
|
|
201
|
+
const { episode, workspace } = this.#prepareKnowledgeCapture(input);
|
|
194
202
|
if (input.targetKind === 'personal') {
|
|
195
|
-
if (input.spaceId !== this.config.personal.spaceId) {
|
|
196
|
-
throw new ApplicationError(
|
|
197
|
-
ApplicationErrorCode.VALIDATION,
|
|
198
|
-
'Personal capture target must be the active personal space'
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
203
|
const result = await this.personal.commit({
|
|
202
204
|
space_id: input.spaceId,
|
|
203
205
|
personal_project_id: input.personalProjectId ?? null,
|
|
@@ -206,20 +208,6 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
206
208
|
});
|
|
207
209
|
return { route: 'personal', ...result };
|
|
208
210
|
}
|
|
209
|
-
if (input.projectAgentId) {
|
|
210
|
-
throw new ApplicationError(
|
|
211
|
-
ApplicationErrorCode.VALIDATION,
|
|
212
|
-
'Project Agent knowledge can only be captured in the personal graph'
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
if (episode.sensitivity !== 'normal') {
|
|
216
|
-
throw new ApplicationError(
|
|
217
|
-
ApplicationErrorCode.VALIDATION,
|
|
218
|
-
'Private or restricted knowledge cannot enter a team-shared project queue. Use targetKind "personal" with personalProjectId to keep it in the personal graph'
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
assertPublicKnowledgeEligible(episode);
|
|
222
|
-
const workspace = this.#workspace(input.providerUrl);
|
|
223
211
|
const draft = await this.personal.createPublicationDraft({
|
|
224
212
|
personal_space_id: this.config.personal.spaceId,
|
|
225
213
|
target_project_id: input.spaceId,
|
|
@@ -257,19 +245,33 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
257
245
|
projectAgentId = null,
|
|
258
246
|
projectPath = null,
|
|
259
247
|
taskPrompt = null,
|
|
248
|
+
sourceApplication = 'other',
|
|
249
|
+
sourceSessionId = null,
|
|
250
|
+
sessionId = null,
|
|
251
|
+
turnId = null,
|
|
252
|
+
workKind = null,
|
|
253
|
+
requiredCapabilities = [],
|
|
260
254
|
limit = 100,
|
|
261
255
|
agentInvocation = false,
|
|
262
256
|
agentToolName = 'get_collaboration_preferences'
|
|
263
257
|
} = {}) {
|
|
264
|
-
const projectResolution = await this.#resolvePreferenceProject({
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
258
|
+
const projectResolution = await this.#resolvePreferenceProject({ personalProjectId, projectPath });
|
|
259
|
+
const selection = projectPath === null && !personalProjectId && !projectAgentId
|
|
260
|
+
? null
|
|
261
|
+
: await resolveTaskEntryAgent(
|
|
262
|
+
this, projectResolution, { projectAgentId, taskPrompt, sourceApplication,
|
|
263
|
+
sessionId: agentToolName === 'get_collaboration_preferences'
|
|
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(
|
|
269
271
|
this,
|
|
270
272
|
projectResolution,
|
|
271
273
|
{
|
|
272
|
-
projectAgentId,
|
|
274
|
+
projectAgentId: selectedAgentId,
|
|
273
275
|
taskPrompt,
|
|
274
276
|
limit,
|
|
275
277
|
agentInvocation,
|
|
@@ -277,6 +279,19 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
277
279
|
},
|
|
278
280
|
(items, toolName) => this.#recordAgentViews(items, toolName)
|
|
279
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 };
|
|
280
295
|
}
|
|
281
296
|
|
|
282
297
|
async getUserTasteSkill({
|
|
@@ -518,7 +533,7 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
518
533
|
|
|
519
534
|
async searchCurrentProjectKnowledge(input) {
|
|
520
535
|
const resolution = await this.#resolvePreferenceProject({
|
|
521
|
-
personalProjectId: null,
|
|
536
|
+
personalProjectId: input.personalProjectId ?? null,
|
|
522
537
|
projectPath: input.projectPath
|
|
523
538
|
});
|
|
524
539
|
return searchCurrentProjectKnowledgeWorkflow(this, resolution, input);
|
|
@@ -1164,7 +1179,7 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
1164
1179
|
};
|
|
1165
1180
|
}
|
|
1166
1181
|
|
|
1167
|
-
close() {}
|
|
1182
|
+
close() { return this.employees?.close(); }
|
|
1168
1183
|
|
|
1169
1184
|
async #recordAgentViews(items, toolName) {
|
|
1170
1185
|
const unique = new Map(items.map((item) => [
|
|
@@ -1195,6 +1210,34 @@ export class FederatedGraphApplication extends ProjectAgentControlPlaneApplicati
|
|
|
1195
1210
|
return this.#resolvePreferenceProject(input);
|
|
1196
1211
|
}
|
|
1197
1212
|
|
|
1213
|
+
#prepareKnowledgeCapture(input) {
|
|
1214
|
+
assertNoCredentials(input);
|
|
1215
|
+
const episode = providerEpisode(input);
|
|
1216
|
+
if (input.targetKind === 'personal') {
|
|
1217
|
+
if (input.spaceId !== this.config.personal.spaceId) {
|
|
1218
|
+
throw new ApplicationError(
|
|
1219
|
+
ApplicationErrorCode.VALIDATION,
|
|
1220
|
+
'Personal capture target must be the active personal space'
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
return { episode };
|
|
1224
|
+
}
|
|
1225
|
+
if (input.projectAgentId) {
|
|
1226
|
+
throw new ApplicationError(
|
|
1227
|
+
ApplicationErrorCode.VALIDATION,
|
|
1228
|
+
'Project Agent knowledge can only be captured in the personal graph'
|
|
1229
|
+
);
|
|
1230
|
+
}
|
|
1231
|
+
if (episode.sensitivity !== 'normal') {
|
|
1232
|
+
throw new ApplicationError(
|
|
1233
|
+
ApplicationErrorCode.VALIDATION,
|
|
1234
|
+
'Private or restricted knowledge cannot enter a team-shared project queue. Use targetKind "personal" with personalProjectId to keep it in the personal graph'
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
assertPublicKnowledgeEligible(episode);
|
|
1238
|
+
return { episode, workspace: this.#workspace(input.providerUrl) };
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1198
1241
|
#workspace(providerUrl) {
|
|
1199
1242
|
const requested = providerUrl
|
|
1200
1243
|
? canonicalProviderUrl(providerUrl)
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { agentProjectResolution } from './agent-knowledge-workflows.js';
|
|
2
|
+
import {
|
|
3
|
+
getProjectAgentMemory,
|
|
4
|
+
checkpointProjectAgentMemory
|
|
5
|
+
} from './project-agent-memory.js';
|
|
2
6
|
import {
|
|
3
7
|
authorizeExecutor as authorizeExecutorWorkflow,
|
|
4
8
|
cleanupProjectAgentTestRoles as cleanupProjectAgentTestRolesWorkflow,
|
|
@@ -48,6 +52,16 @@ export const projectAgentControlPlaneHooks = Object.freeze({
|
|
|
48
52
|
});
|
|
49
53
|
|
|
50
54
|
export class ProjectAgentControlPlaneApplication {
|
|
55
|
+
async getProjectAgentMemory(input) {
|
|
56
|
+
this.#assertSpace(input.personalSpaceId);
|
|
57
|
+
return getProjectAgentMemory(this, input);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async checkpointProjectAgentMemory(input) {
|
|
61
|
+
this.#assertSpace(input.personalSpaceId);
|
|
62
|
+
return checkpointProjectAgentMemory(this, input);
|
|
63
|
+
}
|
|
64
|
+
|
|
51
65
|
async upsertProjectAgent(input) {
|
|
52
66
|
this.#assertSpace(input.personalSpaceId);
|
|
53
67
|
return upsertProjectAgentWorkflow(this, input);
|
|
@@ -252,7 +266,10 @@ export class ProjectAgentControlPlaneApplication {
|
|
|
252
266
|
}
|
|
253
267
|
|
|
254
268
|
async getProjectAgentContext(input) {
|
|
255
|
-
const resolution = await this.#resolveProject(
|
|
269
|
+
const resolution = await this.#resolveProject(
|
|
270
|
+
input.projectPath,
|
|
271
|
+
input.personalProjectId ?? null
|
|
272
|
+
);
|
|
256
273
|
return getProjectAgentContextWorkflow(
|
|
257
274
|
this,
|
|
258
275
|
agentProjectResolution(resolution),
|
|
@@ -266,9 +283,9 @@ export class ProjectAgentControlPlaneApplication {
|
|
|
266
283
|
);
|
|
267
284
|
}
|
|
268
285
|
|
|
269
|
-
#resolveProject(projectPath) {
|
|
286
|
+
#resolveProject(projectPath, personalProjectId = null) {
|
|
270
287
|
return this[projectAgentControlPlaneHooks.resolvePreferenceProject]({
|
|
271
|
-
personalProjectId
|
|
288
|
+
personalProjectId,
|
|
272
289
|
projectPath
|
|
273
290
|
});
|
|
274
291
|
}
|