fuli-context 0.7.4-beta.2 → 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.
- package/README.md +93 -7
- package/README.zh-CN.md +84 -7
- package/compose.graphiti.yml +6 -6
- package/dist/web/assets/AboutPage-ByCUor-j.js +1 -0
- package/dist/web/assets/ConnectionsPage-DkdkZ40Q.js +1 -0
- package/dist/web/assets/KnowledgeEditDialog-DSe3Gwep.js +1 -0
- package/dist/web/assets/KnowledgeInspector-CYAjAb9J.js +1 -0
- package/dist/web/assets/KnowledgeOrganizerPage-CV4F54R_.js +1 -0
- package/dist/web/assets/KnowledgePage-vYYXipOi.js +1 -0
- package/dist/web/assets/KnowledgeWorkspace-z4u5mXOG.js +4 -0
- package/dist/web/assets/OverviewPage-BWOfeS6k.js +1 -0
- package/dist/web/assets/PersonalProfilePage-c3w4-h7a.js +1 -0
- package/dist/web/assets/PersonalProjectsPage-B7AlLEwf.js +1 -0
- package/dist/web/assets/ProjectAgentsPage-CgftpIPB.css +1 -0
- package/dist/web/assets/ProjectAgentsPage-DTHcL_ox.js +11 -0
- package/dist/web/assets/PublicProjectsPage-DdH0oGB3.js +1 -0
- package/dist/web/assets/ReviewPage-BYIpcIHG.js +1 -0
- package/dist/web/assets/SearchableSelect-Dz13ClQb.js +1 -0
- package/dist/web/assets/SettingsPage-2VkbXgZi.js +1 -0
- package/dist/web/assets/SettingsPage-CwZp2ka4.css +1 -0
- package/dist/web/assets/WritingTastePage-BOPQZuva.js +1 -0
- package/dist/web/assets/{console-DMvHxKf9.js → console-BlAhujsH.js} +1 -1
- package/dist/web/assets/{conversation-launcher-settings-Dnlcw7Rj.js → conversation-launcher-settings-DTYxiLJt.js} +1 -1
- package/dist/web/assets/i18n-Ci3heuiL.js +9 -0
- package/dist/web/assets/{identity-C9mLdR8L.js → identity-4_-v6cB7.js} +1 -1
- package/dist/web/assets/index-BnH5mcZx.css +1 -0
- package/dist/web/assets/index-C67Q0JY2.js +2 -0
- package/dist/web/assets/{model-CsYAR8O3.js → model-yG0YqUyZ.js} +1 -1
- package/dist/web/assets/{useApi-CROJJdhE-BLQiPVw0.js → useApi-CROJJdhE-twhdZArA.js} +1 -1
- package/dist/web/assets/vue-router-C-wot_Di.js +1 -0
- package/dist/web/index.html +6 -6
- package/docs/public-personal-architecture.md +73 -0
- package/graph-provider/fuli_graph/app.py +8 -0
- package/graph-provider/fuli_graph/collaboration_context.py +69 -6
- package/graph-provider/fuli_graph/knowledge_search.py +84 -5
- package/graph-provider/fuli_graph/model_validation.py +134 -0
- package/graph-provider/fuli_graph/models.py +24 -5
- package/graph-provider/fuli_graph/project_agent_access.py +54 -0
- package/graph-provider/fuli_graph/project_agent_executor_models.py +635 -0
- package/graph-provider/fuli_graph/project_agent_models.py +278 -0
- package/graph-provider/fuli_graph/project_agent_routes.py +597 -0
- package/graph-provider/fuli_graph/project_agent_task_models.py +542 -0
- package/graph-provider/fuli_graph/runtime.py +259 -0
- package/graph-provider/fuli_graph/search_projection.py +3 -0
- package/graph-provider/fuli_graph/store.py +6 -0
- package/graph-provider/fuli_graph/store_knowledge.py +62 -9
- package/graph-provider/fuli_graph/store_project_agent_executor_learning.py +756 -0
- package/graph-provider/fuli_graph/store_project_agent_executor_routing.py +601 -0
- package/graph-provider/fuli_graph/store_project_agent_executors.py +1087 -0
- package/graph-provider/fuli_graph/store_project_agent_task_reads.py +458 -0
- package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +699 -0
- package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +409 -0
- package/graph-provider/fuli_graph/store_project_agent_tasks.py +1144 -0
- package/graph-provider/fuli_graph/store_project_agents.py +965 -0
- package/graph-provider/pyproject.toml +2 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -1
- package/skills/capturing-session-knowledge/SKILL.md +43 -2
- package/src/adaptive-runtime/executor-pool.js +127 -0
- package/src/adaptive-runtime/graph-services.js +166 -0
- package/src/adaptive-runtime/lease-client.js +335 -0
- package/src/adaptive-runtime/runtime-broker.js +318 -0
- package/src/adaptive-runtime/settings.js +111 -0
- package/src/adaptive-runtime/state.js +74 -0
- package/src/agent-tools/graph-definitions.js +396 -0
- package/src/agent-tools/graph-handlers.js +61 -0
- package/src/agent-tools/project-agent-definitions.js +307 -0
- package/src/backup/graph-backup.js +289 -0
- package/src/backup/identity-reconciliation.js +208 -0
- package/src/backup/runtime-adapters.js +269 -0
- package/src/cli/command-registry.js +7 -2
- package/src/cli/graph-data-command.js +112 -0
- package/src/cli/local-runtime-command.js +16 -2
- package/src/cli/setup-command.js +19 -3
- package/src/cli/update-command.js +4 -0
- package/src/cli/workspace-connection-command.js +333 -0
- package/src/cli.js +11 -0
- package/src/graphiti/collaboration-preference-projection.js +29 -1
- package/src/graphiti/collaboration-preference-workflow.js +121 -0
- package/src/graphiti/federated-application.js +74 -112
- package/src/graphiti/knowledge-provider-mapping.js +7 -1
- package/src/graphiti/project-agent-control-plane.js +263 -0
- package/src/graphiti/project-agent-mapping.js +1003 -0
- package/src/graphiti/project-agent-workflows.js +557 -0
- package/src/graphiti/provider-client.js +270 -1
- package/src/graphiti/runtime-config.js +6 -1
- package/src/graphiti/workspace-provider-client.js +403 -0
- package/src/http/api-router.js +63 -9
- package/src/http/graph-api-router.js +278 -0
- package/src/local-runtime/lifecycle.js +46 -10
- package/src/mcp/context-resources.js +15 -6
- package/src/mcp/create-mcp-server.js +51 -16
- package/src/mcp/instructions.js +7 -5
- package/src/mcp/runtime.js +17 -5
- package/src/mcp/session-id.js +38 -1
- package/src/mcp/tool-annotations.js +69 -0
- package/src/mcp-server.js +23 -6
- package/src/native-runtime/runtime.js +717 -0
- package/src/server.js +6 -2
- package/src/setup/agent-installation-status.js +7 -1
- package/src/setup/agents.js +3 -1
- package/src/setup/claude-code-config.js +3 -1
- package/src/setup/codex-bootstrap.js +11 -10
- package/src/setup/codex-config.js +7 -1
- package/src/setup/cursor-config.js +3 -1
- package/src/setup/graph-runtime.js +158 -13
- package/src/setup/neo4j-memory-profile.js +43 -0
- package/src/setup/options.js +38 -2
- package/src/setup/paths.js +14 -0
- package/src/setup/setup.js +37 -1
- package/src/system/resource-monitor.js +181 -16
- package/src/system/runtime-settings.js +10 -1
- package/src/system/system-service.js +27 -2
- package/dist/web/assets/AboutPage-CftRexSY.js +0 -1
- package/dist/web/assets/ConnectionsPage-D5J91x6d.js +0 -1
- package/dist/web/assets/KnowledgeEditDialog-wotVahvr.js +0 -1
- package/dist/web/assets/KnowledgeInspector-BzkW0mVs.js +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-BebUDiHt.js +0 -1
- package/dist/web/assets/KnowledgePage-OUT07aKH.js +0 -1
- package/dist/web/assets/KnowledgeWorkspace-1pHJ0MNZ.js +0 -4
- package/dist/web/assets/OverviewPage-CHE6XhdS.js +0 -1
- package/dist/web/assets/PersonalProfilePage-CXXiabUy.js +0 -1
- package/dist/web/assets/PersonalProjectsPage-d2wYGW8n.js +0 -1
- package/dist/web/assets/PublicProjectsPage-hxLShtnZ.js +0 -1
- package/dist/web/assets/ReviewPage-DUeyu7E8.js +0 -1
- package/dist/web/assets/SearchableSelect-C5RiB5F8.js +0 -1
- package/dist/web/assets/SettingsPage-BMuASSzQ.js +0 -1
- package/dist/web/assets/SettingsPage-CAfz4qiV.css +0 -1
- package/dist/web/assets/WritingTastePage-B9BCYIwQ.js +0 -1
- package/dist/web/assets/i18n-CWfd8Dtt.js +0 -5
- package/dist/web/assets/index-X2j29j8i.css +0 -1
- package/dist/web/assets/index-nP5p5OaY.js +0 -2
- package/dist/web/assets/vue-router-DN8x6iY1.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(
|
|
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) {
|
|
@@ -38,7 +38,12 @@ function validateConfig(config) {
|
|
|
38
38
|
if (!config.personal.spaceId) fail('personal.spaceId is required');
|
|
39
39
|
if (!Array.isArray(config.workspaces)) fail('workspaces must be an array');
|
|
40
40
|
for (const [index, workspace] of config.workspaces.entries()) {
|
|
41
|
-
|
|
41
|
+
const label = `workspaces[${index}]`;
|
|
42
|
+
provider(workspace, label);
|
|
43
|
+
if (workspace.protocol !== undefined &&
|
|
44
|
+
!['graphiti-v1', 'fuli-workspace-v1'].includes(workspace.protocol)) {
|
|
45
|
+
fail(`${label}.protocol is unsupported`);
|
|
46
|
+
}
|
|
42
47
|
}
|
|
43
48
|
}
|
|
44
49
|
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
GraphitiProviderClient,
|
|
5
|
+
ProviderRequestError
|
|
6
|
+
} from './provider-client.js';
|
|
7
|
+
import { canonicalProviderUrl } from './runtime-config.js';
|
|
8
|
+
|
|
9
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
|
10
|
+
const MAX_REQUEST_TIMEOUT_MS = 2_147_483_647;
|
|
11
|
+
const MAX_LIST_PAGES = 100;
|
|
12
|
+
const FULI_WORKSPACE_PROTOCOL = 'fuli-workspace-v1';
|
|
13
|
+
const GRAPHITI_PROTOCOL = 'graphiti-v1';
|
|
14
|
+
|
|
15
|
+
const GRAPHITI_CAPABILITIES = Object.freeze({
|
|
16
|
+
browsePublicProjects: true,
|
|
17
|
+
publishProject: true,
|
|
18
|
+
submitKnowledge: true,
|
|
19
|
+
subscribeProject: true,
|
|
20
|
+
reviewProposals: true,
|
|
21
|
+
query: true
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const FULI_WORKSPACE_CAPABILITIES = Object.freeze({
|
|
25
|
+
browsePublicProjects: true,
|
|
26
|
+
publishProject: false,
|
|
27
|
+
submitKnowledge: false,
|
|
28
|
+
subscribeProject: true,
|
|
29
|
+
reviewProposals: false,
|
|
30
|
+
query: true
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export function createWorkspaceProvider(workspace, {
|
|
34
|
+
fetchImpl = globalThis.fetch,
|
|
35
|
+
requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS
|
|
36
|
+
} = {}) {
|
|
37
|
+
const providerUrl = canonicalProviderUrl(workspace.providerUrl);
|
|
38
|
+
const protocol = workspace.protocol ?? GRAPHITI_PROTOCOL;
|
|
39
|
+
const fuliWorkspace = protocol === FULI_WORKSPACE_PROTOCOL;
|
|
40
|
+
const client = fuliWorkspace
|
|
41
|
+
? new FuliWorkspaceProviderClient({
|
|
42
|
+
baseUrl: providerUrl,
|
|
43
|
+
accessToken: workspace.accessToken,
|
|
44
|
+
fetchImpl,
|
|
45
|
+
requestTimeoutMs
|
|
46
|
+
})
|
|
47
|
+
: new GraphitiProviderClient({
|
|
48
|
+
baseUrl: providerUrl,
|
|
49
|
+
accessToken: workspace.accessToken,
|
|
50
|
+
fetchImpl,
|
|
51
|
+
requestTimeoutMs
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
...workspace,
|
|
55
|
+
providerUrl,
|
|
56
|
+
protocol,
|
|
57
|
+
capabilities: fuliWorkspace ? FULI_WORKSPACE_CAPABILITIES : GRAPHITI_CAPABILITIES,
|
|
58
|
+
client
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function aggregatePublicCapabilities(workspaces, providerStatuses, projects) {
|
|
63
|
+
const ready = new Set(providerStatuses
|
|
64
|
+
.filter(({ status }) => status === 'ready')
|
|
65
|
+
.map(({ providerUrl }) => providerUrl));
|
|
66
|
+
const supports = (capability) => [...workspaces.values()].some((workspace) =>
|
|
67
|
+
ready.has(workspace.providerUrl) && workspace.capabilities[capability] === true
|
|
68
|
+
);
|
|
69
|
+
return {
|
|
70
|
+
browsePublicProjects: supports('browsePublicProjects'),
|
|
71
|
+
publishProject: supports('publishProject'),
|
|
72
|
+
submitKnowledge: supports('submitKnowledge'),
|
|
73
|
+
subscribeProject: supports('subscribeProject'),
|
|
74
|
+
reviewProposals: supports('reviewProposals') &&
|
|
75
|
+
projects.some(({ role }) => role === 'maintainer')
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export class FuliWorkspaceProviderClient {
|
|
80
|
+
#accessToken;
|
|
81
|
+
#fetch;
|
|
82
|
+
|
|
83
|
+
constructor({
|
|
84
|
+
baseUrl,
|
|
85
|
+
accessToken,
|
|
86
|
+
fetchImpl = globalThis.fetch,
|
|
87
|
+
requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS
|
|
88
|
+
}) {
|
|
89
|
+
if (!baseUrl) throw new TypeError('Fuli Workspace provider baseUrl is required');
|
|
90
|
+
if (!accessToken) throw new TypeError('Fuli Workspace provider accessToken is required');
|
|
91
|
+
if (typeof fetchImpl !== 'function') throw new TypeError('fetch implementation is required');
|
|
92
|
+
if (!Number.isSafeInteger(requestTimeoutMs) || requestTimeoutMs < 1 ||
|
|
93
|
+
requestTimeoutMs > MAX_REQUEST_TIMEOUT_MS) {
|
|
94
|
+
throw new TypeError('Fuli Workspace provider timeout must be a positive safe integer');
|
|
95
|
+
}
|
|
96
|
+
this.baseUrl = canonicalProviderUrl(baseUrl);
|
|
97
|
+
this.#accessToken = accessToken;
|
|
98
|
+
this.#fetch = fetchImpl;
|
|
99
|
+
this.requestTimeoutMs = requestTimeoutMs;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async health() {
|
|
103
|
+
const [payload, session] = await Promise.all([
|
|
104
|
+
this.#request('/healthz', { authenticated: false }),
|
|
105
|
+
this.#request('/v1/auth/session')
|
|
106
|
+
]);
|
|
107
|
+
if (payload?.status !== 'ok' || String(payload?.protocolVersion) !== '1') {
|
|
108
|
+
throw invalidResponse('Fuli Workspace health response is incompatible');
|
|
109
|
+
}
|
|
110
|
+
if (session?.authenticated !== true) {
|
|
111
|
+
throw invalidResponse('Fuli Workspace authentication session is invalid');
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
status: 'ready',
|
|
115
|
+
providerId: 'fuli-workspace',
|
|
116
|
+
mode: 'workspace',
|
|
117
|
+
storage: 'sqlite',
|
|
118
|
+
protocol: FULI_WORKSPACE_PROTOCOL,
|
|
119
|
+
protocolVersion: '1'
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async listSpaces() {
|
|
124
|
+
const workspaces = [];
|
|
125
|
+
let cursor = null;
|
|
126
|
+
for (let page = 0; page < MAX_LIST_PAGES; page += 1) {
|
|
127
|
+
const query = new URLSearchParams({ limit: '500' });
|
|
128
|
+
if (cursor !== null) query.set('cursor', cursor);
|
|
129
|
+
const payload = await this.#request(`/v1/workspaces?${query}`);
|
|
130
|
+
if (!Array.isArray(payload?.workspaces)) {
|
|
131
|
+
throw invalidResponse('Fuli Workspace returned an invalid workspace list');
|
|
132
|
+
}
|
|
133
|
+
workspaces.push(...payload.workspaces.map(workspaceSpace));
|
|
134
|
+
if (payload.hasMore !== true) return workspaces;
|
|
135
|
+
const nextCursor = nonEmptyString(payload.nextCursor);
|
|
136
|
+
if (!nextCursor || nextCursor === cursor) {
|
|
137
|
+
throw invalidResponse('Fuli Workspace returned an invalid list cursor');
|
|
138
|
+
}
|
|
139
|
+
cursor = nextCursor;
|
|
140
|
+
}
|
|
141
|
+
throw invalidResponse('Fuli Workspace workspace listing exceeded the page limit');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async search(input) {
|
|
145
|
+
const spaceIds = uniqueStrings(input?.space_ids);
|
|
146
|
+
const text = nonEmptyString(input?.query);
|
|
147
|
+
if (!spaceIds.length) return { facts: [], entities: [] };
|
|
148
|
+
if (!text || text.length > 512) {
|
|
149
|
+
throw new TypeError('Fuli Workspace search query must contain 1 to 512 characters');
|
|
150
|
+
}
|
|
151
|
+
const limit = boundedInteger(input?.limit, 20, 1, 100);
|
|
152
|
+
const pages = await Promise.all(spaceIds.map(async (spaceId) => {
|
|
153
|
+
const payload = await this.#request(
|
|
154
|
+
`/v1/workspaces/${encodeURIComponent(spaceId)}/query`,
|
|
155
|
+
{
|
|
156
|
+
method: 'POST',
|
|
157
|
+
body: {
|
|
158
|
+
text,
|
|
159
|
+
includeHistory: input?.include_historical === true,
|
|
160
|
+
limit
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
if (!Array.isArray(payload?.facts)) {
|
|
165
|
+
throw invalidResponse('Fuli Workspace returned an invalid query result');
|
|
166
|
+
}
|
|
167
|
+
return payload.facts.map((fact) => searchFact(fact, spaceId));
|
|
168
|
+
}));
|
|
169
|
+
return { facts: pages.flat(), entities: [] };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async graph(spaceId, limit = 500, personalProjectId = null, offset = null) {
|
|
173
|
+
if (personalProjectId !== null) {
|
|
174
|
+
throw new TypeError('Fuli Workspace graphs do not support personal project scope');
|
|
175
|
+
}
|
|
176
|
+
const safeSpaceId = nonEmptyString(spaceId);
|
|
177
|
+
if (!safeSpaceId) throw new TypeError('Fuli Workspace ID is required');
|
|
178
|
+
const pageLimit = boundedInteger(limit, 500, 1, 500);
|
|
179
|
+
const pageOffset = offset === null
|
|
180
|
+
? 0
|
|
181
|
+
: boundedInteger(offset, 0, 0, Number.MAX_SAFE_INTEGER);
|
|
182
|
+
const payload = await this.#request(
|
|
183
|
+
`/v1/workspaces/${encodeURIComponent(safeSpaceId)}/snapshot`
|
|
184
|
+
);
|
|
185
|
+
if (!Array.isArray(payload?.facts)) {
|
|
186
|
+
throw invalidResponse('Fuli Workspace returned an invalid snapshot');
|
|
187
|
+
}
|
|
188
|
+
const selected = payload.facts.slice(pageOffset, pageOffset + pageLimit);
|
|
189
|
+
const graph = factsGraph(selected, safeSpaceId);
|
|
190
|
+
const truncated = pageOffset + selected.length < payload.facts.length;
|
|
191
|
+
return {
|
|
192
|
+
space_id: safeSpaceId,
|
|
193
|
+
...graph,
|
|
194
|
+
truncated,
|
|
195
|
+
next_offset: truncated ? pageOffset + selected.length : null
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
createSpace() { return unsupported(); }
|
|
200
|
+
listProjectReleases() { return unsupported(); }
|
|
201
|
+
deleteProject() { return unsupported(); }
|
|
202
|
+
createProjectRelation() { return unsupported(); }
|
|
203
|
+
listProjectRelations() { return unsupported(); }
|
|
204
|
+
decideProjectRelation() { return unsupported(); }
|
|
205
|
+
createProposal() { return unsupported(); }
|
|
206
|
+
listProposals() { return unsupported(); }
|
|
207
|
+
decideProposal() { return unsupported(); }
|
|
208
|
+
|
|
209
|
+
async #request(path, { method = 'GET', body, authenticated = true } = {}) {
|
|
210
|
+
const url = new URL(path, `${this.baseUrl}/`);
|
|
211
|
+
if (url.origin !== this.baseUrl || !url.pathname.startsWith('/')) {
|
|
212
|
+
throw new TypeError('Fuli Workspace request escaped the configured provider origin');
|
|
213
|
+
}
|
|
214
|
+
const headers = { accept: 'application/json' };
|
|
215
|
+
if (authenticated) headers.authorization = `Bearer ${this.#accessToken}`;
|
|
216
|
+
if (body !== undefined) headers['content-type'] = 'application/json';
|
|
217
|
+
const controller = new AbortController();
|
|
218
|
+
let timedOut = false;
|
|
219
|
+
const timeout = setTimeout(() => {
|
|
220
|
+
timedOut = true;
|
|
221
|
+
controller.abort();
|
|
222
|
+
}, this.requestTimeoutMs);
|
|
223
|
+
try {
|
|
224
|
+
const response = await this.#fetch(url, {
|
|
225
|
+
method,
|
|
226
|
+
headers,
|
|
227
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
228
|
+
redirect: 'error',
|
|
229
|
+
signal: controller.signal
|
|
230
|
+
});
|
|
231
|
+
if (!response.ok) {
|
|
232
|
+
throw new ProviderRequestError(
|
|
233
|
+
`Fuli Workspace provider returned HTTP ${response.status}.`,
|
|
234
|
+
{
|
|
235
|
+
status: response.status,
|
|
236
|
+
code: response.status >= 500 ? 'provider_http_5xx' : 'provider_error'
|
|
237
|
+
}
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
return await safeJson(response);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
if (error instanceof ProviderRequestError) throw error;
|
|
243
|
+
throw new ProviderRequestError(
|
|
244
|
+
timedOut
|
|
245
|
+
? 'Fuli Workspace provider request timed out.'
|
|
246
|
+
: 'Fuli Workspace provider is unavailable.',
|
|
247
|
+
{
|
|
248
|
+
status: timedOut ? 504 : 0,
|
|
249
|
+
code: timedOut ? 'provider_timeout' : 'provider_unavailable'
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
} finally {
|
|
253
|
+
clearTimeout(timeout);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function workspaceSpace(workspace) {
|
|
259
|
+
const id = nonEmptyString(workspace?.id);
|
|
260
|
+
const name = nonEmptyString(workspace?.name);
|
|
261
|
+
if (!id || !name) throw invalidResponse('Fuli Workspace returned an invalid workspace');
|
|
262
|
+
return {
|
|
263
|
+
id,
|
|
264
|
+
name,
|
|
265
|
+
kind: 'project',
|
|
266
|
+
description: typeof workspace.description === 'string' ? workspace.description : null,
|
|
267
|
+
visibility: workspace.visibility ?? null,
|
|
268
|
+
status: workspace.status ?? null,
|
|
269
|
+
current_revision: workspace.revision ?? null,
|
|
270
|
+
created_at: workspace.createdAt ?? null,
|
|
271
|
+
updated_at: workspace.updatedAt ?? null,
|
|
272
|
+
owner_id: null
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function searchFact(fact, spaceId) {
|
|
277
|
+
const triple = factTriple(fact);
|
|
278
|
+
return {
|
|
279
|
+
id: triple.id,
|
|
280
|
+
fact: `${triple.subject} ${triple.predicate} ${triple.object}`,
|
|
281
|
+
source_entity: triple.subject,
|
|
282
|
+
target_entity: triple.object,
|
|
283
|
+
relationship: triple.predicate,
|
|
284
|
+
space_id: spaceId,
|
|
285
|
+
defined_project_id: spaceId,
|
|
286
|
+
scope_distance: 0,
|
|
287
|
+
score: 1,
|
|
288
|
+
origin_quadrant: fact.originQuadrant ?? 'known_known',
|
|
289
|
+
confirmation_status: fact.confirmationStatus ?? 'confirmed',
|
|
290
|
+
valid_at: fact.validFrom ?? fact.createdAt ?? null,
|
|
291
|
+
invalid_at: fact.validTo ?? null,
|
|
292
|
+
created_at: fact.createdAt ?? null
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function factsGraph(facts, spaceId) {
|
|
297
|
+
const nodes = new Map();
|
|
298
|
+
const edges = facts.map((fact) => {
|
|
299
|
+
const triple = factTriple(fact);
|
|
300
|
+
const source = entityNode(spaceId, triple.subject, fact);
|
|
301
|
+
const target = entityNode(spaceId, triple.object, fact);
|
|
302
|
+
nodes.set(source.id, source);
|
|
303
|
+
nodes.set(target.id, target);
|
|
304
|
+
return {
|
|
305
|
+
id: triple.id,
|
|
306
|
+
source: source.id,
|
|
307
|
+
target: target.id,
|
|
308
|
+
source_name: triple.subject,
|
|
309
|
+
target_name: triple.object,
|
|
310
|
+
type: triple.predicate,
|
|
311
|
+
fact: `${triple.subject} ${triple.predicate} ${triple.object}`,
|
|
312
|
+
origin_quadrant: fact.originQuadrant ?? 'known_known',
|
|
313
|
+
current_quadrant: fact.originQuadrant ?? 'known_known',
|
|
314
|
+
epistemic_status: 'confirmed',
|
|
315
|
+
epistemic_state_explicit: true,
|
|
316
|
+
confirmation_status: fact.confirmationStatus ?? 'confirmed',
|
|
317
|
+
confirmation_state_explicit: true,
|
|
318
|
+
profile_aspect: null,
|
|
319
|
+
valid_at: fact.validFrom ?? fact.createdAt ?? null,
|
|
320
|
+
invalid_at: fact.validTo ?? null,
|
|
321
|
+
created_at: fact.createdAt ?? null,
|
|
322
|
+
attributes: {
|
|
323
|
+
workspaceId: spaceId,
|
|
324
|
+
revision: fact.revision ?? null,
|
|
325
|
+
confirmedBy: fact.confirmedBy ?? null,
|
|
326
|
+
sources: Array.isArray(fact.sources) ? fact.sources : []
|
|
327
|
+
},
|
|
328
|
+
evidence: Array.isArray(fact.evidence) ? fact.evidence : []
|
|
329
|
+
};
|
|
330
|
+
});
|
|
331
|
+
return { nodes: [...nodes.values()], edges };
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function entityNode(spaceId, name, fact) {
|
|
335
|
+
return {
|
|
336
|
+
id: `workspace-entity:${stableId(spaceId, name)}`,
|
|
337
|
+
name,
|
|
338
|
+
type: 'WorkspaceEntity',
|
|
339
|
+
group_id: spaceId,
|
|
340
|
+
summary: 'Entity referenced by a confirmed shared workspace fact.',
|
|
341
|
+
origin_quadrant: fact.originQuadrant ?? 'known_known',
|
|
342
|
+
current_quadrant: fact.originQuadrant ?? 'known_known',
|
|
343
|
+
epistemic_status: 'confirmed',
|
|
344
|
+
epistemic_state_explicit: true,
|
|
345
|
+
confirmation_status: fact.confirmationStatus ?? 'confirmed',
|
|
346
|
+
confirmation_state_explicit: true,
|
|
347
|
+
profile_aspect: null,
|
|
348
|
+
created_at: fact.createdAt ?? null,
|
|
349
|
+
invalid_at: null
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function factTriple(fact) {
|
|
354
|
+
const id = nonEmptyString(fact?.id);
|
|
355
|
+
const subject = nonEmptyString(fact?.subject);
|
|
356
|
+
const predicate = nonEmptyString(fact?.predicate);
|
|
357
|
+
const object = nonEmptyString(fact?.object);
|
|
358
|
+
if (!id || !subject || !predicate || !object) {
|
|
359
|
+
throw invalidResponse('Fuli Workspace returned an invalid fact');
|
|
360
|
+
}
|
|
361
|
+
return { id, subject, predicate, object };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function stableId(spaceId, value) {
|
|
365
|
+
return createHash('sha256').update(`${spaceId}\0${value}`, 'utf8').digest('hex').slice(0, 24);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function uniqueStrings(values) {
|
|
369
|
+
if (!Array.isArray(values)) return [];
|
|
370
|
+
return [...new Set(values.map(nonEmptyString).filter(Boolean))];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function nonEmptyString(value) {
|
|
374
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function boundedInteger(value, fallback, minimum, maximum) {
|
|
378
|
+
if (value === undefined || value === null) return fallback;
|
|
379
|
+
if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
|
|
380
|
+
throw new TypeError('Fuli Workspace pagination value is invalid');
|
|
381
|
+
}
|
|
382
|
+
return value;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async function safeJson(response) {
|
|
386
|
+
const type = response.headers.get('content-type') ?? '';
|
|
387
|
+
if (!type.includes('application/json')) {
|
|
388
|
+
throw invalidResponse('Fuli Workspace returned a non-JSON response');
|
|
389
|
+
}
|
|
390
|
+
try {
|
|
391
|
+
return await response.json();
|
|
392
|
+
} catch {
|
|
393
|
+
throw invalidResponse('Fuli Workspace returned invalid JSON');
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function invalidResponse(message) {
|
|
398
|
+
return new ProviderRequestError(message, { code: 'provider_invalid_response' });
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function unsupported() {
|
|
402
|
+
throw new TypeError('Operation is not supported by fuli-workspace-v1');
|
|
403
|
+
}
|