fuli-context 0.7.4-beta.3 → 0.7.6

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.
Files changed (130) hide show
  1. package/README.md +77 -6
  2. package/README.zh-CN.md +69 -6
  3. package/compose.graphiti.yml +6 -6
  4. package/dist/web/assets/AboutPage-sLV9WChi.js +1 -0
  5. package/dist/web/assets/ConnectionsPage-BWn4ywGJ.js +1 -0
  6. package/dist/web/assets/KnowledgeEditDialog-CZ5ziPrD.js +1 -0
  7. package/dist/web/assets/KnowledgeInspector-CRQWOaos.js +1 -0
  8. package/dist/web/assets/KnowledgeOrganizerPage-Dfj3vIpU.js +1 -0
  9. package/dist/web/assets/KnowledgePage-BRYC9S5Y.js +1 -0
  10. package/dist/web/assets/KnowledgeWorkspace-ChEB5Bwk.js +4 -0
  11. package/dist/web/assets/OverviewPage-Cd66M0RT.js +1 -0
  12. package/dist/web/assets/PersonalProfilePage-RCFHL0Wz.js +1 -0
  13. package/dist/web/assets/PersonalProjectsPage-DEh8XWby.js +1 -0
  14. package/dist/web/assets/ProjectAgentsPage-Crh7PrXa.css +1 -0
  15. package/dist/web/assets/ProjectAgentsPage-DPnaH-TR.js +11 -0
  16. package/dist/web/assets/PublicProjectsPage-mstPEjHe.js +1 -0
  17. package/dist/web/assets/ReviewPage-6Y1SmfZy.js +1 -0
  18. package/dist/web/assets/SearchableSelect-D3HM8YF8.js +1 -0
  19. package/dist/web/assets/SettingsPage--LdTMUm7.js +1 -0
  20. package/dist/web/assets/SettingsPage-CwZp2ka4.css +1 -0
  21. package/dist/web/assets/WritingTastePage-CIpKswvs.js +1 -0
  22. package/dist/web/assets/{console-D_2zC4lm.js → console-BT1P2Db4.js} +1 -1
  23. package/dist/web/assets/{conversation-launcher-settings-DQmMH2j7.js → conversation-launcher-settings-DrJEotdf.js} +1 -1
  24. package/dist/web/assets/i18n-js6gfc7j.js +9 -0
  25. package/dist/web/assets/{identity-DmGcY3Y6.js → identity-CfDfanpa.js} +1 -1
  26. package/dist/web/assets/index-BnH5mcZx.css +1 -0
  27. package/dist/web/assets/index-GqJf1xqg.js +2 -0
  28. package/dist/web/assets/{model-CVoOdRsL.js → model-DJkLAJsg.js} +1 -1
  29. package/dist/web/assets/{useApi-CROJJdhE-CBNg4CdH.js → useApi-CROJJdhE-BdSti7Y2.js} +1 -1
  30. package/dist/web/assets/vue-router-Kk67EaCP.js +1 -0
  31. package/dist/web/index.html +6 -6
  32. package/graph-provider/fuli_graph/app.py +8 -0
  33. package/graph-provider/fuli_graph/collaboration_context.py +69 -6
  34. package/graph-provider/fuli_graph/knowledge_search.py +84 -5
  35. package/graph-provider/fuli_graph/model_validation.py +130 -0
  36. package/graph-provider/fuli_graph/models.py +24 -5
  37. package/graph-provider/fuli_graph/project_agent_access.py +54 -0
  38. package/graph-provider/fuli_graph/project_agent_coordination_models.py +22 -0
  39. package/graph-provider/fuli_graph/project_agent_executor_models.py +635 -0
  40. package/graph-provider/fuli_graph/project_agent_models.py +278 -0
  41. package/graph-provider/fuli_graph/project_agent_routes.py +626 -0
  42. package/graph-provider/fuli_graph/project_agent_task_models.py +545 -0
  43. package/graph-provider/fuli_graph/runtime.py +259 -0
  44. package/graph-provider/fuli_graph/search_projection.py +3 -0
  45. package/graph-provider/fuli_graph/store.py +6 -0
  46. package/graph-provider/fuli_graph/store_knowledge.py +62 -9
  47. package/graph-provider/fuli_graph/store_project_agent_coordination_policy.py +122 -0
  48. package/graph-provider/fuli_graph/store_project_agent_executor_learning.py +756 -0
  49. package/graph-provider/fuli_graph/store_project_agent_executor_routing.py +601 -0
  50. package/graph-provider/fuli_graph/store_project_agent_executors.py +1087 -0
  51. package/graph-provider/fuli_graph/store_project_agent_task_reads.py +458 -0
  52. package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +705 -0
  53. package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +586 -0
  54. package/graph-provider/fuli_graph/store_project_agent_tasks.py +1151 -0
  55. package/graph-provider/fuli_graph/store_project_agents.py +965 -0
  56. package/graph-provider/pyproject.toml +2 -1
  57. package/npm-shrinkwrap.json +2 -2
  58. package/package.json +1 -1
  59. package/skills/capturing-session-knowledge/SKILL.md +39 -0
  60. package/src/adaptive-runtime/executor-pool.js +127 -0
  61. package/src/adaptive-runtime/graph-services.js +166 -0
  62. package/src/adaptive-runtime/lease-client.js +335 -0
  63. package/src/adaptive-runtime/runtime-broker.js +318 -0
  64. package/src/adaptive-runtime/settings.js +111 -0
  65. package/src/adaptive-runtime/state.js +74 -0
  66. package/src/agent-tools/graph-definitions.js +419 -0
  67. package/src/agent-tools/graph-handlers.js +65 -0
  68. package/src/agent-tools/project-agent-definitions.js +307 -0
  69. package/src/backup/graph-backup.js +289 -0
  70. package/src/backup/identity-reconciliation.js +208 -0
  71. package/src/backup/runtime-adapters.js +269 -0
  72. package/src/cli/command-registry.js +6 -2
  73. package/src/cli/graph-data-command.js +112 -0
  74. package/src/cli/local-runtime-command.js +16 -2
  75. package/src/cli/setup-command.js +19 -3
  76. package/src/cli/update-command.js +4 -0
  77. package/src/cli.js +6 -0
  78. package/src/graphiti/collaboration-preference-projection.js +29 -1
  79. package/src/graphiti/collaboration-preference-workflow.js +121 -0
  80. package/src/graphiti/federated-application.js +53 -90
  81. package/src/graphiti/project-agent-control-plane.js +275 -0
  82. package/src/graphiti/project-agent-mapping.js +1024 -0
  83. package/src/graphiti/project-agent-workflows.js +576 -0
  84. package/src/graphiti/provider-client.js +282 -1
  85. package/src/http/api-router.js +63 -9
  86. package/src/http/graph-api-router.js +290 -0
  87. package/src/local-runtime/lifecycle.js +46 -10
  88. package/src/mcp/context-resources.js +15 -6
  89. package/src/mcp/create-mcp-server.js +51 -16
  90. package/src/mcp/instructions.js +7 -5
  91. package/src/mcp/runtime.js +17 -5
  92. package/src/mcp/session-id.js +38 -1
  93. package/src/mcp/tool-annotations.js +71 -0
  94. package/src/mcp-server.js +23 -6
  95. package/src/native-runtime/runtime.js +717 -0
  96. package/src/server.js +6 -2
  97. package/src/setup/agent-installation-status.js +7 -1
  98. package/src/setup/agents.js +3 -1
  99. package/src/setup/claude-code-config.js +3 -1
  100. package/src/setup/codex-bootstrap.js +11 -10
  101. package/src/setup/codex-config.js +7 -1
  102. package/src/setup/cursor-config.js +3 -1
  103. package/src/setup/graph-runtime.js +158 -13
  104. package/src/setup/neo4j-memory-profile.js +43 -0
  105. package/src/setup/options.js +38 -2
  106. package/src/setup/paths.js +14 -0
  107. package/src/setup/setup.js +37 -1
  108. package/src/system/resource-monitor.js +181 -16
  109. package/src/system/runtime-settings.js +10 -1
  110. package/src/system/system-service.js +27 -2
  111. package/dist/web/assets/AboutPage-Dpf5fRWv.js +0 -1
  112. package/dist/web/assets/ConnectionsPage-D21aUDCP.js +0 -1
  113. package/dist/web/assets/KnowledgeEditDialog-u7lpfN3j.js +0 -1
  114. package/dist/web/assets/KnowledgeInspector-k4xnHtXd.js +0 -1
  115. package/dist/web/assets/KnowledgeOrganizerPage-Bdj7Rn84.js +0 -1
  116. package/dist/web/assets/KnowledgePage-UHVAbC1W.js +0 -1
  117. package/dist/web/assets/KnowledgeWorkspace-Lq3d-MeC.js +0 -4
  118. package/dist/web/assets/OverviewPage-CycAgA1V.js +0 -1
  119. package/dist/web/assets/PersonalProfilePage-U9oIV5FT.js +0 -1
  120. package/dist/web/assets/PersonalProjectsPage-C5RpXKcA.js +0 -1
  121. package/dist/web/assets/PublicProjectsPage-DiWxm35E.js +0 -1
  122. package/dist/web/assets/ReviewPage-jxRfGSJK.js +0 -1
  123. package/dist/web/assets/SearchableSelect-B3-5IPup.js +0 -1
  124. package/dist/web/assets/SettingsPage-BqfGDOho.js +0 -1
  125. package/dist/web/assets/SettingsPage-CAfz4qiV.css +0 -1
  126. package/dist/web/assets/WritingTastePage-BJxhMqJR.js +0 -1
  127. package/dist/web/assets/i18n-CwqXDI1C.js +0 -5
  128. package/dist/web/assets/index-IUGLt4lG.js +0 -2
  129. package/dist/web/assets/index-X2j29j8i.css +0 -1
  130. package/dist/web/assets/vue-router-CWSsUUio.js +0 -1
@@ -79,6 +79,281 @@ export class GraphitiProviderClient {
79
79
  `/v1/personal-projects/${encodeURIComponent(projectId)}?${query}`
80
80
  );
81
81
  }
82
+ upsertProjectAgent(input) {
83
+ return this.#request('/v1/project-agents', { method: 'PUT', body: input });
84
+ }
85
+ listProjectAgents(
86
+ personalSpaceId,
87
+ personalProjectId = null,
88
+ { status = null, capability = null } = {}
89
+ ) {
90
+ const query = new URLSearchParams({
91
+ personal_space_id: personalSpaceId
92
+ });
93
+ if (personalProjectId) query.set('personal_project_id', personalProjectId);
94
+ if (status) query.set('status', status);
95
+ if (capability) query.set('capability', capability);
96
+ return this.#request(`/v1/project-agents?${query}`);
97
+ }
98
+ getProjectAgent(personalSpaceId, personalProjectId, agentId) {
99
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
100
+ if (personalProjectId) query.set('personal_project_id', personalProjectId);
101
+ return this.#request(
102
+ `/v1/project-agents/${encodeURIComponent(agentId)}?${query}`
103
+ );
104
+ }
105
+ deleteProjectAgent(personalSpaceId, agentId, reason = 'archived by user') {
106
+ const query = new URLSearchParams({
107
+ personal_space_id: personalSpaceId,
108
+ reason
109
+ });
110
+ return this.#request(
111
+ `/v1/project-agents/${encodeURIComponent(agentId)}?${query}`,
112
+ { method: 'DELETE' }
113
+ );
114
+ }
115
+ cleanupProjectAgentTestRoles(personalSpaceId, testSource) {
116
+ const query = new URLSearchParams({
117
+ personal_space_id: personalSpaceId,
118
+ test_source: testSource
119
+ });
120
+ return this.#request(`/v1/project-agents/test-cleanup?${query}`, {
121
+ method: 'POST'
122
+ });
123
+ }
124
+ createProjectAgentAssignment(input) {
125
+ return this.#request('/v1/project-agent-assignments', {
126
+ method: 'POST', body: input
127
+ });
128
+ }
129
+ listProjectAgentAssignments({
130
+ personalSpaceId,
131
+ personalProjectId = null,
132
+ agentId = null,
133
+ status = null
134
+ }) {
135
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
136
+ if (personalProjectId) query.set('personal_project_id', personalProjectId);
137
+ if (agentId) query.set('agent_id', agentId);
138
+ if (status) query.set('status', status);
139
+ return this.#request(`/v1/project-agent-assignments?${query}`);
140
+ }
141
+ endProjectAgentAssignment(input) {
142
+ return this.#request('/v1/project-agent-assignments/end', {
143
+ method: 'POST', body: input
144
+ });
145
+ }
146
+ replaceProjectAgentAssignment(input) {
147
+ return this.#request('/v1/project-agent-assignments/replace', {
148
+ method: 'POST', body: input
149
+ });
150
+ }
151
+ submitProjectAgentTask(input) {
152
+ return this.#request('/v1/project-agent-tasks', {
153
+ method: 'POST', body: input
154
+ });
155
+ }
156
+ listProjectAgentTasks({
157
+ personalSpaceId,
158
+ personalProjectId = null,
159
+ agentId = null,
160
+ status = null,
161
+ limit = null
162
+ }) {
163
+ const query = new URLSearchParams({
164
+ personal_space_id: personalSpaceId
165
+ });
166
+ if (personalProjectId) query.set('personal_project_id', personalProjectId);
167
+ if (agentId) query.set('agent_id', agentId);
168
+ if (status) query.set('status', status);
169
+ if (limit !== null && limit !== undefined) query.set('limit', String(limit));
170
+ return this.#request(`/v1/project-agent-tasks?${query}`);
171
+ }
172
+ viewProjectAgentTask(personalSpaceId, taskId, { includeEvents = true } = {}) {
173
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
174
+ query.set('include_events', String(includeEvents));
175
+ return this.#request(
176
+ `/v1/project-agent-tasks/${encodeURIComponent(taskId)}?${query}`
177
+ );
178
+ }
179
+ recordProjectAgentTaskActivity(input) {
180
+ return this.#request(
181
+ `/v1/project-agent-tasks/${encodeURIComponent(input.task_id ?? input.taskId)}/events`,
182
+ {
183
+ method: 'POST', body: input
184
+ }
185
+ );
186
+ }
187
+ listProjectAgentActivity({
188
+ personalSpaceId,
189
+ agentId,
190
+ fromDate = null,
191
+ toDate = null
192
+ }) {
193
+ const today = new Date().toISOString().slice(0, 10);
194
+ const query = new URLSearchParams({
195
+ personal_space_id: personalSpaceId,
196
+ from: fromDate ?? toDate ?? today,
197
+ to: toDate ?? fromDate ?? today
198
+ });
199
+ return this.#request(
200
+ `/v1/project-agents/${encodeURIComponent(agentId)}/activity?${query}`
201
+ );
202
+ }
203
+ getProjectAgentCoordinationPolicy(personalSpaceId, personalProjectId) {
204
+ const query = new URLSearchParams({
205
+ personal_space_id: personalSpaceId,
206
+ personal_project_id: personalProjectId
207
+ });
208
+ return this.#request(`/v1/project-agent-coordination-policy?${query}`);
209
+ }
210
+ updateProjectAgentCoordinationPolicy(input) {
211
+ return this.#request('/v1/project-agent-coordination-policy', {
212
+ method: 'PUT', body: input
213
+ });
214
+ }
215
+ getProjectAgentRecruitmentPolicy(personalSpaceId) {
216
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
217
+ return this.#request(`/v1/project-agent-recruitment-policy?${query}`);
218
+ }
219
+ updateProjectAgentRecruitmentPolicy(input) {
220
+ return this.#request('/v1/project-agent-recruitment-policy', {
221
+ method: 'PUT', body: input
222
+ });
223
+ }
224
+ listProjectAgentRecruitments({
225
+ personalSpaceId,
226
+ personalProjectId = null,
227
+ taskId = null,
228
+ status = null
229
+ }) {
230
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
231
+ if (personalProjectId) query.set('personal_project_id', personalProjectId);
232
+ if (taskId) query.set('task_id', taskId);
233
+ if (status) query.set('status', status);
234
+ return this.#request(`/v1/project-agent-recruitments?${query}`);
235
+ }
236
+ decideProjectAgentRecruitment(input) {
237
+ return this.#request(
238
+ `/v1/project-agent-recruitments/${encodeURIComponent(
239
+ input.recruitment_id ?? input.recruitmentId
240
+ )}/decision`,
241
+ { method: 'POST', body: input }
242
+ );
243
+ }
244
+ upsertExecutor(input) {
245
+ return this.#request('/v1/executors', { method: 'PUT', body: input });
246
+ }
247
+ listExecutors(
248
+ personalSpaceId,
249
+ { capability = null, availableOnly = false } = {}
250
+ ) {
251
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
252
+ if (capability) query.set('capability', capability);
253
+ if (availableOnly) query.set('available_only', 'true');
254
+ return this.#request(`/v1/executors?${query}`);
255
+ }
256
+ getExecutor(personalSpaceId, executorId) {
257
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
258
+ return this.#request(`/v1/executors/${encodeURIComponent(executorId)}?${query}`);
259
+ }
260
+ deleteExecutor(personalSpaceId, executorId) {
261
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
262
+ return this.#request(`/v1/executors/${encodeURIComponent(executorId)}?${query}`, {
263
+ method: 'DELETE'
264
+ });
265
+ }
266
+ preflightExecutor(input) {
267
+ return this.#request('/v1/executors/preflight', { method: 'POST', body: input });
268
+ }
269
+ authorizeExecutor(input) {
270
+ return this.#request('/v1/executors/authorization', {
271
+ method: 'POST', body: input
272
+ });
273
+ }
274
+ reportExecutorHealth(input) {
275
+ return this.#request('/v1/executors/health', {
276
+ method: 'POST', body: input
277
+ });
278
+ }
279
+ recordProjectAgentExecutorActual(input) {
280
+ return this.#request('/v1/project-agent-executor-actuals', {
281
+ method: 'POST', body: input
282
+ });
283
+ }
284
+ upsertExecutorRoutingRule(input) {
285
+ return this.#request('/v1/executor-routing-rules', {
286
+ method: 'PUT', body: input
287
+ });
288
+ }
289
+ updateExecutorRoutingRule(input) {
290
+ return this.#request(
291
+ `/v1/executor-routing-rules/${encodeURIComponent(
292
+ input.rule_id ?? input.ruleId
293
+ )}`,
294
+ { method: 'PATCH', body: input }
295
+ );
296
+ }
297
+ listExecutorRoutingRules({
298
+ personalSpaceId,
299
+ scope = null,
300
+ personalProjectId = null,
301
+ taskId = null,
302
+ status = null,
303
+ enabled = null
304
+ }) {
305
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
306
+ if (scope) query.set('scope', scope);
307
+ if (personalProjectId) query.set('personal_project_id', personalProjectId);
308
+ if (taskId) query.set('task_id', taskId);
309
+ if (status) query.set('status', status);
310
+ else if (enabled !== null && enabled !== undefined) {
311
+ query.set('status', enabled ? 'active' : 'disabled');
312
+ }
313
+ return this.#request(`/v1/executor-routing-rules?${query}`);
314
+ }
315
+ getExecutorRoutingRule(personalSpaceId, ruleId) {
316
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
317
+ return this.#request(
318
+ `/v1/executor-routing-rules/${encodeURIComponent(ruleId)}?${query}`
319
+ );
320
+ }
321
+ deleteExecutorRoutingRule(personalSpaceId, ruleId) {
322
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
323
+ return this.#request(
324
+ `/v1/executor-routing-rules/${encodeURIComponent(ruleId)}?${query}`,
325
+ { method: 'DELETE' }
326
+ );
327
+ }
328
+ recordProjectAgentTaskOutcome(input) {
329
+ return this.#request('/v1/project-agent-routing-outcomes', {
330
+ method: 'POST', body: input
331
+ });
332
+ }
333
+ listProjectAgentRoutingLearning({
334
+ personalSpaceId,
335
+ personalProjectId = null,
336
+ workKind = null,
337
+ agentId = null,
338
+ executorId = null
339
+ }) {
340
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
341
+ if (personalProjectId) query.set('personal_project_id', personalProjectId);
342
+ if (workKind) query.set('work_kind', workKind);
343
+ if (agentId) query.set('agent_id', agentId);
344
+ if (executorId) query.set('executor_id', executorId);
345
+ return this.#request(`/v1/project-agent-routing-learning?${query}`);
346
+ }
347
+ ignoreProjectAgentRoutingLearning(input) {
348
+ return this.#request('/v1/project-agent-routing-learning/ignore', {
349
+ method: 'POST', body: input
350
+ });
351
+ }
352
+ resetProjectAgentRoutingLearning(input) {
353
+ return this.#request('/v1/project-agent-routing-learning/reset', {
354
+ method: 'POST', body: input
355
+ });
356
+ }
82
357
  createPublicationDraft(input) {
83
358
  return this.#request('/v1/publication-drafts', { method: 'POST', body: input });
84
359
  }
@@ -320,12 +595,18 @@ export class GraphitiProviderClient {
320
595
  );
321
596
  }
322
597
  search(input) { return this.#request('/v1/search', { method: 'POST', body: input }); }
323
- collaborationPreferences(personalSpaceId, personalProjectId = null, limit = 100) {
598
+ collaborationPreferences(
599
+ personalSpaceId,
600
+ personalProjectId = null,
601
+ limit = 100,
602
+ projectAgentId = null
603
+ ) {
324
604
  const query = new URLSearchParams({
325
605
  personal_space_id: personalSpaceId,
326
606
  limit: String(limit)
327
607
  });
328
608
  if (personalProjectId) query.set('personal_project_id', personalProjectId);
609
+ if (projectAgentId) query.set('project_agent_id', projectAgentId);
329
610
  return this.#request(`/v1/collaboration-preferences?${query}`);
330
611
  }
331
612
  graph(spaceId, limit = 500, personalProjectId = null, offset = null) {
@@ -36,13 +36,67 @@ export async function handleApiRequest({
36
36
  return true;
37
37
  }
38
38
 
39
- if (await handleExternalKnowledgeApiRequest({
40
- request,
41
- response,
42
- url,
43
- externalKnowledge,
44
- connectedKnowledge
45
- })) return true;
46
-
47
- return handleGraphApiRequest({ request, response, url, app });
39
+ if (system && url.pathname === '/api/system/runtime' && request.method === 'GET') {
40
+ sendJson(response, 200, system.runtimeStatus());
41
+ return true;
42
+ }
43
+
44
+ if (system && url.pathname === '/api/system/runtime/leases' && request.method === 'POST') {
45
+ sendJson(response, 201, await system.acquireRuntimeLease(await readJson(request)));
46
+ return true;
47
+ }
48
+
49
+ const runtimeLease = url.pathname.match(/^\/api\/system\/runtime\/leases\/([^/]+)$/);
50
+ if (system && runtimeLease && request.method === 'PATCH') {
51
+ sendJson(
52
+ response,
53
+ 200,
54
+ system.refreshRuntimeLease(decodeURIComponent(runtimeLease[1]))
55
+ );
56
+ return true;
57
+ }
58
+ if (system && runtimeLease && request.method === 'DELETE') {
59
+ sendJson(
60
+ response,
61
+ 200,
62
+ system.releaseRuntimeLease(decodeURIComponent(runtimeLease[1]))
63
+ );
64
+ return true;
65
+ }
66
+
67
+ const externalRequest = () => handleExternalKnowledgeApiRequest({
68
+ request, response, url, externalKnowledge, connectedKnowledge
69
+ });
70
+ const handledExternal = system?.withGraphRuntimeLease &&
71
+ externalRequestNeedsGraph(url.pathname, request.method)
72
+ ? await system.withGraphRuntimeLease(
73
+ `http:${request.method}:${url.pathname}`,
74
+ externalRequest
75
+ )
76
+ : await externalRequest();
77
+ if (handledExternal) return true;
78
+
79
+ const graphRequest = () => handleGraphApiRequest({ request, response, url, app });
80
+ if (system?.withGraphRuntimeLease && url.pathname.startsWith('/api/')) {
81
+ return system.withGraphRuntimeLease(
82
+ `http:${request.method}:${url.pathname}`,
83
+ graphRequest
84
+ );
85
+ }
86
+ return graphRequest();
87
+ }
88
+
89
+ function externalRequestNeedsGraph(pathname, method) {
90
+ if (pathname === '/api/connected-knowledge/search' && method === 'POST') return true;
91
+ if (pathname === '/api/external-knowledge/bindings' && method === 'POST') return true;
92
+ if (
93
+ /^\/api\/external-knowledge\/bindings\/[^/]+\/sync$/.test(pathname) &&
94
+ method === 'POST'
95
+ ) return true;
96
+ if (
97
+ /^\/api\/external-knowledge\/bindings\/[^/]+\/targets$/.test(pathname) &&
98
+ method === 'PATCH'
99
+ ) return true;
100
+ return /^\/api\/external-knowledge\/bindings\/[^/]+$/.test(pathname) &&
101
+ method === 'DELETE';
48
102
  }
@@ -178,6 +178,296 @@ export async function handleGraphApiRequest({
178
178
  sendJson(response, 200, await app.upsertPersonalProject(await readJson(request)));
179
179
  return true;
180
180
  }
181
+ if (url.pathname === '/api/project-agents' && request.method === 'GET') {
182
+ sendJson(response, 200, await app.listProjectAgents({
183
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
184
+ personalProjectId: url.searchParams.get('personalProjectId'),
185
+ status: url.searchParams.get('status'),
186
+ capability: url.searchParams.get('capability')
187
+ }));
188
+ return true;
189
+ }
190
+ if (url.pathname === '/api/project-agents' && request.method === 'PUT') {
191
+ sendJson(response, 200, await app.upsertProjectAgent(await readJson(request)));
192
+ return true;
193
+ }
194
+ if (url.pathname === '/api/project-agent-context' && request.method === 'POST') {
195
+ sendJson(response, 200, await app.getProjectAgentContext(await readJson(request)));
196
+ return true;
197
+ }
198
+ const projectAgentPath = url.pathname.match(/^\/api\/project-agents\/([^/]+)$/);
199
+ if (projectAgentPath && request.method === 'GET') {
200
+ sendJson(response, 200, await app.getProjectAgent({
201
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
202
+ personalProjectId: url.searchParams.get('personalProjectId'),
203
+ agentId: decodeURIComponent(projectAgentPath[1])
204
+ }));
205
+ return true;
206
+ }
207
+ if (projectAgentPath && request.method === 'DELETE') {
208
+ sendJson(response, 200, await app.deleteProjectAgent({
209
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
210
+ agentId: decodeURIComponent(projectAgentPath[1]),
211
+ reason: url.searchParams.get('reason') ?? 'archived by user'
212
+ }));
213
+ return true;
214
+ }
215
+ if (url.pathname === '/api/project-agents/test-cleanup' && request.method === 'POST') {
216
+ sendJson(response, 200, await app.cleanupProjectAgentTestRoles({
217
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
218
+ testSource: url.searchParams.get('testSource')
219
+ }));
220
+ return true;
221
+ }
222
+ if (url.pathname === '/api/project-agent-assignments' && request.method === 'GET') {
223
+ sendJson(response, 200, await app.listProjectAgentAssignments({
224
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
225
+ personalProjectId: url.searchParams.get('personalProjectId'),
226
+ agentId: url.searchParams.get('agentId'),
227
+ status: url.searchParams.get('status')
228
+ }));
229
+ return true;
230
+ }
231
+ if (url.pathname === '/api/project-agent-assignments' && request.method === 'POST') {
232
+ sendJson(response, 200, await app.createProjectAgentAssignment(await readJson(request)));
233
+ return true;
234
+ }
235
+ const projectAgentAssignmentPath = url.pathname.match(
236
+ /^\/api\/project-agent-assignments\/([^/]+)\/(end|replace)$/
237
+ );
238
+ if (projectAgentAssignmentPath && request.method === 'POST') {
239
+ const body = await readJson(request);
240
+ const input = {
241
+ ...body,
242
+ assignmentId: decodeURIComponent(projectAgentAssignmentPath[1])
243
+ };
244
+ const result = projectAgentAssignmentPath[2] === 'end'
245
+ ? await app.endProjectAgentAssignment(input)
246
+ : await app.replaceProjectAgentAssignment(input);
247
+ sendJson(response, 200, result);
248
+ return true;
249
+ }
250
+ if (url.pathname === '/api/project-agent-tasks' && request.method === 'GET') {
251
+ sendJson(response, 200, await app.listProjectAgentTasks({
252
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
253
+ personalProjectId: url.searchParams.get('personalProjectId'),
254
+ agentId: url.searchParams.get('agentId'),
255
+ status: url.searchParams.get('status'),
256
+ limit: numberParam(url, 'limit', null)
257
+ }));
258
+ return true;
259
+ }
260
+ if (url.pathname === '/api/project-agent-tasks' && request.method === 'POST') {
261
+ sendJson(response, 200, await app.submitProjectAgentTask(await readJson(request)));
262
+ return true;
263
+ }
264
+ const projectAgentTaskPath = url.pathname.match(/^\/api\/project-agent-tasks\/([^/]+)$/);
265
+ if (projectAgentTaskPath && request.method === 'GET') {
266
+ sendJson(response, 200, await app.viewProjectAgentTask({
267
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
268
+ taskId: decodeURIComponent(projectAgentTaskPath[1]),
269
+ includeEvents: url.searchParams.get('includeEvents') !== 'false'
270
+ }));
271
+ return true;
272
+ }
273
+ if (url.pathname === '/api/project-agent-activity' && request.method === 'GET') {
274
+ sendJson(response, 200, await app.viewProjectAgentActivity({
275
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
276
+ agentId: url.searchParams.get('agentId'),
277
+ fromDate: url.searchParams.get('fromDate'),
278
+ toDate: url.searchParams.get('toDate')
279
+ }));
280
+ return true;
281
+ }
282
+ if (url.pathname === '/api/project-agent-activity' && request.method === 'POST') {
283
+ sendJson(response, 200, await app.recordProjectAgentTaskActivity(await readJson(request)));
284
+ return true;
285
+ }
286
+ if (url.pathname === '/api/project-agent-coordination-policy' && request.method === 'GET') {
287
+ sendJson(response, 200, await app.getProjectAgentCoordinationPolicy({
288
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
289
+ personalProjectId: url.searchParams.get('personalProjectId')
290
+ }));
291
+ return true;
292
+ }
293
+ if (url.pathname === '/api/project-agent-coordination-policy' &&
294
+ (request.method === 'PUT' || request.method === 'PATCH')) {
295
+ sendJson(response, 200, await app.updateProjectAgentCoordinationPolicy(await readJson(request)));
296
+ return true;
297
+ }
298
+ if (url.pathname === '/api/project-agent-recruitment-policy' && request.method === 'GET') {
299
+ sendJson(response, 200, await app.getProjectAgentRecruitmentPolicy({
300
+ personalSpaceId: url.searchParams.get('personalSpaceId')
301
+ }));
302
+ return true;
303
+ }
304
+ if (url.pathname === '/api/project-agent-recruitment-policy' &&
305
+ (request.method === 'PUT' || request.method === 'PATCH')) {
306
+ sendJson(response, 200, await app.updateProjectAgentRecruitmentPolicy(await readJson(request)));
307
+ return true;
308
+ }
309
+ if (url.pathname === '/api/project-agent-recruitments' && request.method === 'GET') {
310
+ sendJson(response, 200, await app.listProjectAgentRecruitments({
311
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
312
+ personalProjectId: url.searchParams.get('personalProjectId'),
313
+ taskId: url.searchParams.get('taskId'),
314
+ status: url.searchParams.get('status')
315
+ }));
316
+ return true;
317
+ }
318
+ if (url.pathname === '/api/project-agent-recruitments/decision' && request.method === 'POST') {
319
+ sendJson(response, 200, await app.decideProjectAgentRecruitment(await readJson(request)));
320
+ return true;
321
+ }
322
+ if (url.pathname === '/api/executors' && request.method === 'GET') {
323
+ sendJson(response, 200, await app.listExecutors({
324
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
325
+ capability: url.searchParams.get('capability'),
326
+ availableOnly: url.searchParams.get('availableOnly') === 'true'
327
+ }));
328
+ return true;
329
+ }
330
+ if (url.pathname === '/api/executors' && request.method === 'PUT') {
331
+ sendJson(response, 200, await app.upsertExecutor(await readJson(request)));
332
+ return true;
333
+ }
334
+ const executorPath = url.pathname.match(/^\/api\/executors\/([^/]+)$/);
335
+ if (executorPath && request.method === 'GET') {
336
+ sendJson(response, 200, await app.getExecutor({
337
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
338
+ executorId: decodeURIComponent(executorPath[1])
339
+ }));
340
+ return true;
341
+ }
342
+ if (executorPath && request.method === 'DELETE') {
343
+ sendJson(response, 200, await app.deleteExecutor({
344
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
345
+ executorId: decodeURIComponent(executorPath[1])
346
+ }));
347
+ return true;
348
+ }
349
+ if (url.pathname === '/api/executors/preflight' && request.method === 'POST') {
350
+ sendJson(response, 200, await app.preflightExecutor(await readJson(request)));
351
+ return true;
352
+ }
353
+ if (url.pathname === '/api/executors/authorization' && request.method === 'POST') {
354
+ sendJson(response, 200, await app.authorizeExecutor(await readJson(request)));
355
+ return true;
356
+ }
357
+ if (url.pathname === '/api/executors/health' && request.method === 'POST') {
358
+ sendJson(response, 200, await app.reportExecutorHealth(await readJson(request)));
359
+ return true;
360
+ }
361
+ if (url.pathname === '/api/project-agent-executor-actuals' && request.method === 'POST') {
362
+ sendJson(response, 200, await app.recordProjectAgentExecutorActual(await readJson(request)));
363
+ return true;
364
+ }
365
+ if (url.pathname === '/api/executor-routing-rules' && request.method === 'GET') {
366
+ sendJson(response, 200, await app.listExecutorRoutingRules({
367
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
368
+ scope: url.searchParams.get('scope'),
369
+ personalProjectId: url.searchParams.get('personalProjectId'),
370
+ taskId: url.searchParams.get('taskId'),
371
+ status: url.searchParams.get('status')
372
+ }));
373
+ return true;
374
+ }
375
+ if (url.pathname === '/api/executor-routing-rules' && request.method === 'PUT') {
376
+ sendJson(response, 200, await app.upsertExecutorRoutingRule(await readJson(request)));
377
+ return true;
378
+ }
379
+ const executorRoutingRulePath = url.pathname.match(
380
+ /^\/api\/executor-routing-rules\/([^/]+)$/
381
+ );
382
+ if (executorRoutingRulePath && request.method === 'GET') {
383
+ sendJson(response, 200, await app.getExecutorRoutingRule({
384
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
385
+ ruleId: decodeURIComponent(executorRoutingRulePath[1])
386
+ }));
387
+ return true;
388
+ }
389
+ if (executorRoutingRulePath && request.method === 'PATCH') {
390
+ const body = await readJson(request);
391
+ sendJson(response, 200, await app.updateExecutorRoutingRule({
392
+ ...body,
393
+ personalSpaceId: body.personalSpaceId ?? url.searchParams.get('personalSpaceId'),
394
+ ruleId: decodeURIComponent(executorRoutingRulePath[1])
395
+ }));
396
+ return true;
397
+ }
398
+ if (executorRoutingRulePath && request.method === 'DELETE') {
399
+ sendJson(response, 200, await app.deleteExecutorRoutingRule({
400
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
401
+ ruleId: decodeURIComponent(executorRoutingRulePath[1])
402
+ }));
403
+ return true;
404
+ }
405
+ if (url.pathname === '/api/project-agent-routing-learning' && request.method === 'GET') {
406
+ sendJson(response, 200, await app.listProjectAgentRoutingLearning({
407
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
408
+ personalProjectId: url.searchParams.get('personalProjectId'),
409
+ workKind: url.searchParams.get('workKind'),
410
+ agentId: url.searchParams.get('agentId'),
411
+ executorId: url.searchParams.get('executorId')
412
+ }));
413
+ return true;
414
+ }
415
+ const projectAgentLearningPath = url.pathname.match(
416
+ /^\/api\/project-agent-learning\/([^/]+)$/
417
+ );
418
+ if (projectAgentLearningPath && request.method === 'PATCH') {
419
+ const body = await readJson(request);
420
+ const evidenceId = decodeURIComponent(projectAgentLearningPath[1]);
421
+ const action = body.action;
422
+ if (!['ignore', 'reset'].includes(action)) {
423
+ throw new TypeError('Project Agent learning action must be ignore or reset');
424
+ }
425
+ const personalSpaceId = body.personalSpaceId ??
426
+ url.searchParams.get('personalSpaceId') ?? app.config?.personal?.spaceId;
427
+ const personalProjectId = body.personalProjectId ??
428
+ url.searchParams.get('personalProjectId');
429
+ const agentId = body.agentId ?? url.searchParams.get('agentId');
430
+ if (!personalSpaceId || !personalProjectId || !agentId) {
431
+ throw new TypeError(
432
+ 'Project Agent learning updates require personalSpaceId, personalProjectId, and agentId'
433
+ );
434
+ }
435
+ const idempotencyKey = body.idempotencyKey ??
436
+ `console-learning:${evidenceId}:${action}`;
437
+ const reason = body.reason ?? `Console requested ${action} for explicit evidence`;
438
+ const input = {
439
+ ...body,
440
+ personalSpaceId,
441
+ personalProjectId,
442
+ agentId,
443
+ evidenceId,
444
+ idempotencyKey,
445
+ reason
446
+ };
447
+ if (action === 'ignore') {
448
+ sendJson(response, 200, await app.ignoreProjectAgentRoutingLearning(input));
449
+ return true;
450
+ }
451
+ if (!input.workKind || !input.executorId || !input.resetAt) {
452
+ throw new TypeError(
453
+ 'Project Agent learning reset requires workKind, executorId, and resetAt'
454
+ );
455
+ }
456
+ sendJson(response, 200, await app.resetProjectAgentRoutingLearning(input));
457
+ return true;
458
+ }
459
+ if (url.pathname === '/api/project-agent-routing-learning/ignore' && request.method === 'POST') {
460
+ sendJson(response, 200, await app.ignoreProjectAgentRoutingLearning(await readJson(request)));
461
+ return true;
462
+ }
463
+ if (url.pathname === '/api/project-agent-routing-learning/reset' && request.method === 'POST') {
464
+ sendJson(response, 200, await app.resetProjectAgentRoutingLearning(await readJson(request)));
465
+ return true;
466
+ }
467
+ if (url.pathname === '/api/project-agent-routing-outcomes' && request.method === 'POST') {
468
+ sendJson(response, 200, await app.recordProjectAgentTaskOutcome(await readJson(request)));
469
+ return true;
470
+ }
181
471
  if (url.pathname === '/api/projects/publish' && request.method === 'POST') {
182
472
  sendJson(response, 200, await app.publishPersonalProject(await readJson(request)));
183
473
  return true;