fuli-context 0.7.4-beta.3 → 0.7.5

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 (128) 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-ByCUor-j.js +1 -0
  5. package/dist/web/assets/ConnectionsPage-DkdkZ40Q.js +1 -0
  6. package/dist/web/assets/KnowledgeEditDialog-DSe3Gwep.js +1 -0
  7. package/dist/web/assets/KnowledgeInspector-CYAjAb9J.js +1 -0
  8. package/dist/web/assets/KnowledgeOrganizerPage-CV4F54R_.js +1 -0
  9. package/dist/web/assets/KnowledgePage-vYYXipOi.js +1 -0
  10. package/dist/web/assets/KnowledgeWorkspace-z4u5mXOG.js +4 -0
  11. package/dist/web/assets/OverviewPage-BWOfeS6k.js +1 -0
  12. package/dist/web/assets/PersonalProfilePage-c3w4-h7a.js +1 -0
  13. package/dist/web/assets/PersonalProjectsPage-B7AlLEwf.js +1 -0
  14. package/dist/web/assets/ProjectAgentsPage-CgftpIPB.css +1 -0
  15. package/dist/web/assets/ProjectAgentsPage-DTHcL_ox.js +11 -0
  16. package/dist/web/assets/PublicProjectsPage-DdH0oGB3.js +1 -0
  17. package/dist/web/assets/ReviewPage-BYIpcIHG.js +1 -0
  18. package/dist/web/assets/SearchableSelect-Dz13ClQb.js +1 -0
  19. package/dist/web/assets/SettingsPage-2VkbXgZi.js +1 -0
  20. package/dist/web/assets/SettingsPage-CwZp2ka4.css +1 -0
  21. package/dist/web/assets/WritingTastePage-BOPQZuva.js +1 -0
  22. package/dist/web/assets/{console-D_2zC4lm.js → console-BlAhujsH.js} +1 -1
  23. package/dist/web/assets/{conversation-launcher-settings-DQmMH2j7.js → conversation-launcher-settings-DTYxiLJt.js} +1 -1
  24. package/dist/web/assets/i18n-Ci3heuiL.js +9 -0
  25. package/dist/web/assets/{identity-DmGcY3Y6.js → identity-4_-v6cB7.js} +1 -1
  26. package/dist/web/assets/index-BnH5mcZx.css +1 -0
  27. package/dist/web/assets/index-C67Q0JY2.js +2 -0
  28. package/dist/web/assets/{model-CVoOdRsL.js → model-yG0YqUyZ.js} +1 -1
  29. package/dist/web/assets/{useApi-CROJJdhE-CBNg4CdH.js → useApi-CROJJdhE-twhdZArA.js} +1 -1
  30. package/dist/web/assets/vue-router-C-wot_Di.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_executor_models.py +635 -0
  39. package/graph-provider/fuli_graph/project_agent_models.py +278 -0
  40. package/graph-provider/fuli_graph/project_agent_routes.py +597 -0
  41. package/graph-provider/fuli_graph/project_agent_task_models.py +542 -0
  42. package/graph-provider/fuli_graph/runtime.py +259 -0
  43. package/graph-provider/fuli_graph/search_projection.py +3 -0
  44. package/graph-provider/fuli_graph/store.py +6 -0
  45. package/graph-provider/fuli_graph/store_knowledge.py +62 -9
  46. package/graph-provider/fuli_graph/store_project_agent_executor_learning.py +756 -0
  47. package/graph-provider/fuli_graph/store_project_agent_executor_routing.py +601 -0
  48. package/graph-provider/fuli_graph/store_project_agent_executors.py +1087 -0
  49. package/graph-provider/fuli_graph/store_project_agent_task_reads.py +458 -0
  50. package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +699 -0
  51. package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +409 -0
  52. package/graph-provider/fuli_graph/store_project_agent_tasks.py +1144 -0
  53. package/graph-provider/fuli_graph/store_project_agents.py +965 -0
  54. package/graph-provider/pyproject.toml +2 -1
  55. package/npm-shrinkwrap.json +2 -2
  56. package/package.json +1 -1
  57. package/skills/capturing-session-knowledge/SKILL.md +39 -0
  58. package/src/adaptive-runtime/executor-pool.js +127 -0
  59. package/src/adaptive-runtime/graph-services.js +166 -0
  60. package/src/adaptive-runtime/lease-client.js +335 -0
  61. package/src/adaptive-runtime/runtime-broker.js +318 -0
  62. package/src/adaptive-runtime/settings.js +111 -0
  63. package/src/adaptive-runtime/state.js +74 -0
  64. package/src/agent-tools/graph-definitions.js +396 -0
  65. package/src/agent-tools/graph-handlers.js +61 -0
  66. package/src/agent-tools/project-agent-definitions.js +307 -0
  67. package/src/backup/graph-backup.js +289 -0
  68. package/src/backup/identity-reconciliation.js +208 -0
  69. package/src/backup/runtime-adapters.js +269 -0
  70. package/src/cli/command-registry.js +6 -2
  71. package/src/cli/graph-data-command.js +112 -0
  72. package/src/cli/local-runtime-command.js +16 -2
  73. package/src/cli/setup-command.js +19 -3
  74. package/src/cli/update-command.js +4 -0
  75. package/src/cli.js +6 -0
  76. package/src/graphiti/collaboration-preference-projection.js +29 -1
  77. package/src/graphiti/collaboration-preference-workflow.js +121 -0
  78. package/src/graphiti/federated-application.js +53 -90
  79. package/src/graphiti/project-agent-control-plane.js +263 -0
  80. package/src/graphiti/project-agent-mapping.js +1003 -0
  81. package/src/graphiti/project-agent-workflows.js +557 -0
  82. package/src/graphiti/provider-client.js +270 -1
  83. package/src/http/api-router.js +63 -9
  84. package/src/http/graph-api-router.js +278 -0
  85. package/src/local-runtime/lifecycle.js +46 -10
  86. package/src/mcp/context-resources.js +15 -6
  87. package/src/mcp/create-mcp-server.js +51 -16
  88. package/src/mcp/instructions.js +7 -5
  89. package/src/mcp/runtime.js +17 -5
  90. package/src/mcp/session-id.js +38 -1
  91. package/src/mcp/tool-annotations.js +69 -0
  92. package/src/mcp-server.js +23 -6
  93. package/src/native-runtime/runtime.js +717 -0
  94. package/src/server.js +6 -2
  95. package/src/setup/agent-installation-status.js +7 -1
  96. package/src/setup/agents.js +3 -1
  97. package/src/setup/claude-code-config.js +3 -1
  98. package/src/setup/codex-bootstrap.js +11 -10
  99. package/src/setup/codex-config.js +7 -1
  100. package/src/setup/cursor-config.js +3 -1
  101. package/src/setup/graph-runtime.js +158 -13
  102. package/src/setup/neo4j-memory-profile.js +43 -0
  103. package/src/setup/options.js +38 -2
  104. package/src/setup/paths.js +14 -0
  105. package/src/setup/setup.js +37 -1
  106. package/src/system/resource-monitor.js +181 -16
  107. package/src/system/runtime-settings.js +10 -1
  108. package/src/system/system-service.js +27 -2
  109. package/dist/web/assets/AboutPage-Dpf5fRWv.js +0 -1
  110. package/dist/web/assets/ConnectionsPage-D21aUDCP.js +0 -1
  111. package/dist/web/assets/KnowledgeEditDialog-u7lpfN3j.js +0 -1
  112. package/dist/web/assets/KnowledgeInspector-k4xnHtXd.js +0 -1
  113. package/dist/web/assets/KnowledgeOrganizerPage-Bdj7Rn84.js +0 -1
  114. package/dist/web/assets/KnowledgePage-UHVAbC1W.js +0 -1
  115. package/dist/web/assets/KnowledgeWorkspace-Lq3d-MeC.js +0 -4
  116. package/dist/web/assets/OverviewPage-CycAgA1V.js +0 -1
  117. package/dist/web/assets/PersonalProfilePage-U9oIV5FT.js +0 -1
  118. package/dist/web/assets/PersonalProjectsPage-C5RpXKcA.js +0 -1
  119. package/dist/web/assets/PublicProjectsPage-DiWxm35E.js +0 -1
  120. package/dist/web/assets/ReviewPage-jxRfGSJK.js +0 -1
  121. package/dist/web/assets/SearchableSelect-B3-5IPup.js +0 -1
  122. package/dist/web/assets/SettingsPage-BqfGDOho.js +0 -1
  123. package/dist/web/assets/SettingsPage-CAfz4qiV.css +0 -1
  124. package/dist/web/assets/WritingTastePage-BJxhMqJR.js +0 -1
  125. package/dist/web/assets/i18n-CwqXDI1C.js +0 -5
  126. package/dist/web/assets/index-IUGLt4lG.js +0 -2
  127. package/dist/web/assets/index-X2j29j8i.css +0 -1
  128. package/dist/web/assets/vue-router-CWSsUUio.js +0 -1
@@ -79,6 +79,269 @@ 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
+ getProjectAgentRecruitmentPolicy(personalSpaceId) {
204
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
205
+ return this.#request(`/v1/project-agent-recruitment-policy?${query}`);
206
+ }
207
+ updateProjectAgentRecruitmentPolicy(input) {
208
+ return this.#request('/v1/project-agent-recruitment-policy', {
209
+ method: 'PUT', body: input
210
+ });
211
+ }
212
+ listProjectAgentRecruitments({
213
+ personalSpaceId,
214
+ personalProjectId = null,
215
+ taskId = null,
216
+ status = null
217
+ }) {
218
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
219
+ if (personalProjectId) query.set('personal_project_id', personalProjectId);
220
+ if (taskId) query.set('task_id', taskId);
221
+ if (status) query.set('status', status);
222
+ return this.#request(`/v1/project-agent-recruitments?${query}`);
223
+ }
224
+ decideProjectAgentRecruitment(input) {
225
+ return this.#request(
226
+ `/v1/project-agent-recruitments/${encodeURIComponent(
227
+ input.recruitment_id ?? input.recruitmentId
228
+ )}/decision`,
229
+ { method: 'POST', body: input }
230
+ );
231
+ }
232
+ upsertExecutor(input) {
233
+ return this.#request('/v1/executors', { method: 'PUT', body: input });
234
+ }
235
+ listExecutors(
236
+ personalSpaceId,
237
+ { capability = null, availableOnly = false } = {}
238
+ ) {
239
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
240
+ if (capability) query.set('capability', capability);
241
+ if (availableOnly) query.set('available_only', 'true');
242
+ return this.#request(`/v1/executors?${query}`);
243
+ }
244
+ getExecutor(personalSpaceId, executorId) {
245
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
246
+ return this.#request(`/v1/executors/${encodeURIComponent(executorId)}?${query}`);
247
+ }
248
+ deleteExecutor(personalSpaceId, executorId) {
249
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
250
+ return this.#request(`/v1/executors/${encodeURIComponent(executorId)}?${query}`, {
251
+ method: 'DELETE'
252
+ });
253
+ }
254
+ preflightExecutor(input) {
255
+ return this.#request('/v1/executors/preflight', { method: 'POST', body: input });
256
+ }
257
+ authorizeExecutor(input) {
258
+ return this.#request('/v1/executors/authorization', {
259
+ method: 'POST', body: input
260
+ });
261
+ }
262
+ reportExecutorHealth(input) {
263
+ return this.#request('/v1/executors/health', {
264
+ method: 'POST', body: input
265
+ });
266
+ }
267
+ recordProjectAgentExecutorActual(input) {
268
+ return this.#request('/v1/project-agent-executor-actuals', {
269
+ method: 'POST', body: input
270
+ });
271
+ }
272
+ upsertExecutorRoutingRule(input) {
273
+ return this.#request('/v1/executor-routing-rules', {
274
+ method: 'PUT', body: input
275
+ });
276
+ }
277
+ updateExecutorRoutingRule(input) {
278
+ return this.#request(
279
+ `/v1/executor-routing-rules/${encodeURIComponent(
280
+ input.rule_id ?? input.ruleId
281
+ )}`,
282
+ { method: 'PATCH', body: input }
283
+ );
284
+ }
285
+ listExecutorRoutingRules({
286
+ personalSpaceId,
287
+ scope = null,
288
+ personalProjectId = null,
289
+ taskId = null,
290
+ status = null,
291
+ enabled = null
292
+ }) {
293
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
294
+ if (scope) query.set('scope', scope);
295
+ if (personalProjectId) query.set('personal_project_id', personalProjectId);
296
+ if (taskId) query.set('task_id', taskId);
297
+ if (status) query.set('status', status);
298
+ else if (enabled !== null && enabled !== undefined) {
299
+ query.set('status', enabled ? 'active' : 'disabled');
300
+ }
301
+ return this.#request(`/v1/executor-routing-rules?${query}`);
302
+ }
303
+ getExecutorRoutingRule(personalSpaceId, ruleId) {
304
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
305
+ return this.#request(
306
+ `/v1/executor-routing-rules/${encodeURIComponent(ruleId)}?${query}`
307
+ );
308
+ }
309
+ deleteExecutorRoutingRule(personalSpaceId, ruleId) {
310
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
311
+ return this.#request(
312
+ `/v1/executor-routing-rules/${encodeURIComponent(ruleId)}?${query}`,
313
+ { method: 'DELETE' }
314
+ );
315
+ }
316
+ recordProjectAgentTaskOutcome(input) {
317
+ return this.#request('/v1/project-agent-routing-outcomes', {
318
+ method: 'POST', body: input
319
+ });
320
+ }
321
+ listProjectAgentRoutingLearning({
322
+ personalSpaceId,
323
+ personalProjectId = null,
324
+ workKind = null,
325
+ agentId = null,
326
+ executorId = null
327
+ }) {
328
+ const query = new URLSearchParams({ personal_space_id: personalSpaceId });
329
+ if (personalProjectId) query.set('personal_project_id', personalProjectId);
330
+ if (workKind) query.set('work_kind', workKind);
331
+ if (agentId) query.set('agent_id', agentId);
332
+ if (executorId) query.set('executor_id', executorId);
333
+ return this.#request(`/v1/project-agent-routing-learning?${query}`);
334
+ }
335
+ ignoreProjectAgentRoutingLearning(input) {
336
+ return this.#request('/v1/project-agent-routing-learning/ignore', {
337
+ method: 'POST', body: input
338
+ });
339
+ }
340
+ resetProjectAgentRoutingLearning(input) {
341
+ return this.#request('/v1/project-agent-routing-learning/reset', {
342
+ method: 'POST', body: input
343
+ });
344
+ }
82
345
  createPublicationDraft(input) {
83
346
  return this.#request('/v1/publication-drafts', { method: 'POST', body: input });
84
347
  }
@@ -320,12 +583,18 @@ export class GraphitiProviderClient {
320
583
  );
321
584
  }
322
585
  search(input) { return this.#request('/v1/search', { method: 'POST', body: input }); }
323
- collaborationPreferences(personalSpaceId, personalProjectId = null, limit = 100) {
586
+ collaborationPreferences(
587
+ personalSpaceId,
588
+ personalProjectId = null,
589
+ limit = 100,
590
+ projectAgentId = null
591
+ ) {
324
592
  const query = new URLSearchParams({
325
593
  personal_space_id: personalSpaceId,
326
594
  limit: String(limit)
327
595
  });
328
596
  if (personalProjectId) query.set('personal_project_id', personalProjectId);
597
+ if (projectAgentId) query.set('project_agent_id', projectAgentId);
329
598
  return this.#request(`/v1/collaboration-preferences?${query}`);
330
599
  }
331
600
  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,284 @@ 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-recruitment-policy' && request.method === 'GET') {
287
+ sendJson(response, 200, await app.getProjectAgentRecruitmentPolicy({
288
+ personalSpaceId: url.searchParams.get('personalSpaceId')
289
+ }));
290
+ return true;
291
+ }
292
+ if (url.pathname === '/api/project-agent-recruitment-policy' &&
293
+ (request.method === 'PUT' || request.method === 'PATCH')) {
294
+ sendJson(response, 200, await app.updateProjectAgentRecruitmentPolicy(await readJson(request)));
295
+ return true;
296
+ }
297
+ if (url.pathname === '/api/project-agent-recruitments' && request.method === 'GET') {
298
+ sendJson(response, 200, await app.listProjectAgentRecruitments({
299
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
300
+ personalProjectId: url.searchParams.get('personalProjectId'),
301
+ taskId: url.searchParams.get('taskId'),
302
+ status: url.searchParams.get('status')
303
+ }));
304
+ return true;
305
+ }
306
+ if (url.pathname === '/api/project-agent-recruitments/decision' && request.method === 'POST') {
307
+ sendJson(response, 200, await app.decideProjectAgentRecruitment(await readJson(request)));
308
+ return true;
309
+ }
310
+ if (url.pathname === '/api/executors' && request.method === 'GET') {
311
+ sendJson(response, 200, await app.listExecutors({
312
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
313
+ capability: url.searchParams.get('capability'),
314
+ availableOnly: url.searchParams.get('availableOnly') === 'true'
315
+ }));
316
+ return true;
317
+ }
318
+ if (url.pathname === '/api/executors' && request.method === 'PUT') {
319
+ sendJson(response, 200, await app.upsertExecutor(await readJson(request)));
320
+ return true;
321
+ }
322
+ const executorPath = url.pathname.match(/^\/api\/executors\/([^/]+)$/);
323
+ if (executorPath && request.method === 'GET') {
324
+ sendJson(response, 200, await app.getExecutor({
325
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
326
+ executorId: decodeURIComponent(executorPath[1])
327
+ }));
328
+ return true;
329
+ }
330
+ if (executorPath && request.method === 'DELETE') {
331
+ sendJson(response, 200, await app.deleteExecutor({
332
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
333
+ executorId: decodeURIComponent(executorPath[1])
334
+ }));
335
+ return true;
336
+ }
337
+ if (url.pathname === '/api/executors/preflight' && request.method === 'POST') {
338
+ sendJson(response, 200, await app.preflightExecutor(await readJson(request)));
339
+ return true;
340
+ }
341
+ if (url.pathname === '/api/executors/authorization' && request.method === 'POST') {
342
+ sendJson(response, 200, await app.authorizeExecutor(await readJson(request)));
343
+ return true;
344
+ }
345
+ if (url.pathname === '/api/executors/health' && request.method === 'POST') {
346
+ sendJson(response, 200, await app.reportExecutorHealth(await readJson(request)));
347
+ return true;
348
+ }
349
+ if (url.pathname === '/api/project-agent-executor-actuals' && request.method === 'POST') {
350
+ sendJson(response, 200, await app.recordProjectAgentExecutorActual(await readJson(request)));
351
+ return true;
352
+ }
353
+ if (url.pathname === '/api/executor-routing-rules' && request.method === 'GET') {
354
+ sendJson(response, 200, await app.listExecutorRoutingRules({
355
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
356
+ scope: url.searchParams.get('scope'),
357
+ personalProjectId: url.searchParams.get('personalProjectId'),
358
+ taskId: url.searchParams.get('taskId'),
359
+ status: url.searchParams.get('status')
360
+ }));
361
+ return true;
362
+ }
363
+ if (url.pathname === '/api/executor-routing-rules' && request.method === 'PUT') {
364
+ sendJson(response, 200, await app.upsertExecutorRoutingRule(await readJson(request)));
365
+ return true;
366
+ }
367
+ const executorRoutingRulePath = url.pathname.match(
368
+ /^\/api\/executor-routing-rules\/([^/]+)$/
369
+ );
370
+ if (executorRoutingRulePath && request.method === 'GET') {
371
+ sendJson(response, 200, await app.getExecutorRoutingRule({
372
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
373
+ ruleId: decodeURIComponent(executorRoutingRulePath[1])
374
+ }));
375
+ return true;
376
+ }
377
+ if (executorRoutingRulePath && request.method === 'PATCH') {
378
+ const body = await readJson(request);
379
+ sendJson(response, 200, await app.updateExecutorRoutingRule({
380
+ ...body,
381
+ personalSpaceId: body.personalSpaceId ?? url.searchParams.get('personalSpaceId'),
382
+ ruleId: decodeURIComponent(executorRoutingRulePath[1])
383
+ }));
384
+ return true;
385
+ }
386
+ if (executorRoutingRulePath && request.method === 'DELETE') {
387
+ sendJson(response, 200, await app.deleteExecutorRoutingRule({
388
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
389
+ ruleId: decodeURIComponent(executorRoutingRulePath[1])
390
+ }));
391
+ return true;
392
+ }
393
+ if (url.pathname === '/api/project-agent-routing-learning' && request.method === 'GET') {
394
+ sendJson(response, 200, await app.listProjectAgentRoutingLearning({
395
+ personalSpaceId: url.searchParams.get('personalSpaceId'),
396
+ personalProjectId: url.searchParams.get('personalProjectId'),
397
+ workKind: url.searchParams.get('workKind'),
398
+ agentId: url.searchParams.get('agentId'),
399
+ executorId: url.searchParams.get('executorId')
400
+ }));
401
+ return true;
402
+ }
403
+ const projectAgentLearningPath = url.pathname.match(
404
+ /^\/api\/project-agent-learning\/([^/]+)$/
405
+ );
406
+ if (projectAgentLearningPath && request.method === 'PATCH') {
407
+ const body = await readJson(request);
408
+ const evidenceId = decodeURIComponent(projectAgentLearningPath[1]);
409
+ const action = body.action;
410
+ if (!['ignore', 'reset'].includes(action)) {
411
+ throw new TypeError('Project Agent learning action must be ignore or reset');
412
+ }
413
+ const personalSpaceId = body.personalSpaceId ??
414
+ url.searchParams.get('personalSpaceId') ?? app.config?.personal?.spaceId;
415
+ const personalProjectId = body.personalProjectId ??
416
+ url.searchParams.get('personalProjectId');
417
+ const agentId = body.agentId ?? url.searchParams.get('agentId');
418
+ if (!personalSpaceId || !personalProjectId || !agentId) {
419
+ throw new TypeError(
420
+ 'Project Agent learning updates require personalSpaceId, personalProjectId, and agentId'
421
+ );
422
+ }
423
+ const idempotencyKey = body.idempotencyKey ??
424
+ `console-learning:${evidenceId}:${action}`;
425
+ const reason = body.reason ?? `Console requested ${action} for explicit evidence`;
426
+ const input = {
427
+ ...body,
428
+ personalSpaceId,
429
+ personalProjectId,
430
+ agentId,
431
+ evidenceId,
432
+ idempotencyKey,
433
+ reason
434
+ };
435
+ if (action === 'ignore') {
436
+ sendJson(response, 200, await app.ignoreProjectAgentRoutingLearning(input));
437
+ return true;
438
+ }
439
+ if (!input.workKind || !input.executorId || !input.resetAt) {
440
+ throw new TypeError(
441
+ 'Project Agent learning reset requires workKind, executorId, and resetAt'
442
+ );
443
+ }
444
+ sendJson(response, 200, await app.resetProjectAgentRoutingLearning(input));
445
+ return true;
446
+ }
447
+ if (url.pathname === '/api/project-agent-routing-learning/ignore' && request.method === 'POST') {
448
+ sendJson(response, 200, await app.ignoreProjectAgentRoutingLearning(await readJson(request)));
449
+ return true;
450
+ }
451
+ if (url.pathname === '/api/project-agent-routing-learning/reset' && request.method === 'POST') {
452
+ sendJson(response, 200, await app.resetProjectAgentRoutingLearning(await readJson(request)));
453
+ return true;
454
+ }
455
+ if (url.pathname === '/api/project-agent-routing-outcomes' && request.method === 'POST') {
456
+ sendJson(response, 200, await app.recordProjectAgentTaskOutcome(await readJson(request)));
457
+ return true;
458
+ }
181
459
  if (url.pathname === '/api/projects/publish' && request.method === 'POST') {
182
460
  sendJson(response, 200, await app.publishPersonalProject(await readJson(request)));
183
461
  return true;