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.
Files changed (133) hide show
  1. package/README.md +93 -7
  2. package/README.zh-CN.md +84 -7
  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-DMvHxKf9.js → console-BlAhujsH.js} +1 -1
  23. package/dist/web/assets/{conversation-launcher-settings-Dnlcw7Rj.js → conversation-launcher-settings-DTYxiLJt.js} +1 -1
  24. package/dist/web/assets/i18n-Ci3heuiL.js +9 -0
  25. package/dist/web/assets/{identity-C9mLdR8L.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-CsYAR8O3.js → model-yG0YqUyZ.js} +1 -1
  29. package/dist/web/assets/{useApi-CROJJdhE-BLQiPVw0.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/docs/public-personal-architecture.md +73 -0
  33. package/graph-provider/fuli_graph/app.py +8 -0
  34. package/graph-provider/fuli_graph/collaboration_context.py +69 -6
  35. package/graph-provider/fuli_graph/knowledge_search.py +84 -5
  36. package/graph-provider/fuli_graph/model_validation.py +134 -0
  37. package/graph-provider/fuli_graph/models.py +24 -5
  38. package/graph-provider/fuli_graph/project_agent_access.py +54 -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 +597 -0
  42. package/graph-provider/fuli_graph/project_agent_task_models.py +542 -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_executor_learning.py +756 -0
  48. package/graph-provider/fuli_graph/store_project_agent_executor_routing.py +601 -0
  49. package/graph-provider/fuli_graph/store_project_agent_executors.py +1087 -0
  50. package/graph-provider/fuli_graph/store_project_agent_task_reads.py +458 -0
  51. package/graph-provider/fuli_graph/store_project_agent_task_recruitment.py +699 -0
  52. package/graph-provider/fuli_graph/store_project_agent_task_staffing.py +409 -0
  53. package/graph-provider/fuli_graph/store_project_agent_tasks.py +1144 -0
  54. package/graph-provider/fuli_graph/store_project_agents.py +965 -0
  55. package/graph-provider/pyproject.toml +2 -1
  56. package/npm-shrinkwrap.json +2 -2
  57. package/package.json +2 -1
  58. package/skills/capturing-session-knowledge/SKILL.md +43 -2
  59. package/src/adaptive-runtime/executor-pool.js +127 -0
  60. package/src/adaptive-runtime/graph-services.js +166 -0
  61. package/src/adaptive-runtime/lease-client.js +335 -0
  62. package/src/adaptive-runtime/runtime-broker.js +318 -0
  63. package/src/adaptive-runtime/settings.js +111 -0
  64. package/src/adaptive-runtime/state.js +74 -0
  65. package/src/agent-tools/graph-definitions.js +396 -0
  66. package/src/agent-tools/graph-handlers.js +61 -0
  67. package/src/agent-tools/project-agent-definitions.js +307 -0
  68. package/src/backup/graph-backup.js +289 -0
  69. package/src/backup/identity-reconciliation.js +208 -0
  70. package/src/backup/runtime-adapters.js +269 -0
  71. package/src/cli/command-registry.js +7 -2
  72. package/src/cli/graph-data-command.js +112 -0
  73. package/src/cli/local-runtime-command.js +16 -2
  74. package/src/cli/setup-command.js +19 -3
  75. package/src/cli/update-command.js +4 -0
  76. package/src/cli/workspace-connection-command.js +333 -0
  77. package/src/cli.js +11 -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 +74 -112
  81. package/src/graphiti/knowledge-provider-mapping.js +7 -1
  82. package/src/graphiti/project-agent-control-plane.js +263 -0
  83. package/src/graphiti/project-agent-mapping.js +1003 -0
  84. package/src/graphiti/project-agent-workflows.js +557 -0
  85. package/src/graphiti/provider-client.js +270 -1
  86. package/src/graphiti/runtime-config.js +6 -1
  87. package/src/graphiti/workspace-provider-client.js +403 -0
  88. package/src/http/api-router.js +63 -9
  89. package/src/http/graph-api-router.js +278 -0
  90. package/src/local-runtime/lifecycle.js +46 -10
  91. package/src/mcp/context-resources.js +15 -6
  92. package/src/mcp/create-mcp-server.js +51 -16
  93. package/src/mcp/instructions.js +7 -5
  94. package/src/mcp/runtime.js +17 -5
  95. package/src/mcp/session-id.js +38 -1
  96. package/src/mcp/tool-annotations.js +69 -0
  97. package/src/mcp-server.js +23 -6
  98. package/src/native-runtime/runtime.js +717 -0
  99. package/src/server.js +6 -2
  100. package/src/setup/agent-installation-status.js +7 -1
  101. package/src/setup/agents.js +3 -1
  102. package/src/setup/claude-code-config.js +3 -1
  103. package/src/setup/codex-bootstrap.js +11 -10
  104. package/src/setup/codex-config.js +7 -1
  105. package/src/setup/cursor-config.js +3 -1
  106. package/src/setup/graph-runtime.js +158 -13
  107. package/src/setup/neo4j-memory-profile.js +43 -0
  108. package/src/setup/options.js +38 -2
  109. package/src/setup/paths.js +14 -0
  110. package/src/setup/setup.js +37 -1
  111. package/src/system/resource-monitor.js +181 -16
  112. package/src/system/runtime-settings.js +10 -1
  113. package/src/system/system-service.js +27 -2
  114. package/dist/web/assets/AboutPage-CftRexSY.js +0 -1
  115. package/dist/web/assets/ConnectionsPage-D5J91x6d.js +0 -1
  116. package/dist/web/assets/KnowledgeEditDialog-wotVahvr.js +0 -1
  117. package/dist/web/assets/KnowledgeInspector-BzkW0mVs.js +0 -1
  118. package/dist/web/assets/KnowledgeOrganizerPage-BebUDiHt.js +0 -1
  119. package/dist/web/assets/KnowledgePage-OUT07aKH.js +0 -1
  120. package/dist/web/assets/KnowledgeWorkspace-1pHJ0MNZ.js +0 -4
  121. package/dist/web/assets/OverviewPage-CHE6XhdS.js +0 -1
  122. package/dist/web/assets/PersonalProfilePage-CXXiabUy.js +0 -1
  123. package/dist/web/assets/PersonalProjectsPage-d2wYGW8n.js +0 -1
  124. package/dist/web/assets/PublicProjectsPage-hxLShtnZ.js +0 -1
  125. package/dist/web/assets/ReviewPage-DUeyu7E8.js +0 -1
  126. package/dist/web/assets/SearchableSelect-C5RiB5F8.js +0 -1
  127. package/dist/web/assets/SettingsPage-BMuASSzQ.js +0 -1
  128. package/dist/web/assets/SettingsPage-CAfz4qiV.css +0 -1
  129. package/dist/web/assets/WritingTastePage-B9BCYIwQ.js +0 -1
  130. package/dist/web/assets/i18n-CWfd8Dtt.js +0 -5
  131. package/dist/web/assets/index-X2j29j8i.css +0 -1
  132. package/dist/web/assets/index-nP5p5OaY.js +0 -2
  133. package/dist/web/assets/vue-router-DN8x6iY1.js +0 -1
@@ -1,11 +1,12 @@
1
1
  [project]
2
2
  name = "fuli-graph-provider"
3
- version = "0.7.4b1"
3
+ version = "0.7.5"
4
4
  description = "Graphiti-backed personal and shared project provider for Fuli."
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [
7
7
  "fastapi==0.116.1",
8
8
  "graphiti-core==0.29.2",
9
+ "openai>=1.91,<2",
9
10
  "pydantic-settings==2.10.1",
10
11
  "uvicorn[standard]==0.35.0",
11
12
  ]
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "fuli-context",
3
- "version": "0.7.4-beta.2",
3
+ "version": "0.7.5",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "fuli-context",
9
- "version": "0.7.4-beta.2",
9
+ "version": "0.7.5",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@hono/node-server": "2.0.12",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fuli-context",
3
- "version": "0.7.4-beta.2",
3
+ "version": "0.7.5",
4
4
  "description": "Local-first collaboration relationship graph for AI agents.",
5
5
  "type": "module",
6
6
  "files": [
@@ -9,6 +9,7 @@
9
9
  "src/",
10
10
  "dist/web/",
11
11
  "docs/external-knowledge-architecture.md",
12
+ "docs/public-personal-architecture.md",
12
13
  "examples/external-knowledge/markdown-folder.mjs",
13
14
  "graph-provider/Dockerfile",
14
15
  "graph-provider/pyproject.toml",
@@ -213,6 +213,45 @@ lowers ranking and marks the item for attention without deleting its history. Ag
213
213
  must not demote human/source-confirmed knowledge; authoritative contradiction may require review
214
214
  or demotion under the Provider policy.
215
215
 
216
+ ## Coordinate Project Agents
217
+
218
+ Use a project Agent team only when the user requests delegation or the host's active delegation
219
+ policy authorizes it and the task has independently verifiable work. Keep one primary coordinator
220
+ responsible for decisions, integration, and final verification. Do not split a small or tightly
221
+ coupled task merely to create more workers.
222
+
223
+ For an eligible task, call `coordinate_project_agent_task` once with the exact current
224
+ `projectPath`, one stable idempotency key, an explicit complexity hint when known, and focused
225
+ context queries. Enable its parallel plan only when there are at least two conflict-free
226
+ workstreams with independent verification; state each workstream boundary. The Provider chooses
227
+ the lead and collaborators from active project assignments, preferring Agents with relevant
228
+ project history. Explicit user-selected Agents and locked policies remain higher priority.
229
+
230
+ Treat the returned durable Agent identity and a host worker as different things. A durable Agent
231
+ keeps its project assignment, preferences, task history, and Agent-scoped graph memory while idle;
232
+ the host creates an execution process only when needed. Start only entries in `worker_plan`, give
233
+ each worker only its returned isolated `context`, role, and workstream boundary, and never expose
234
+ another Agent's private context. Fuli is the control plane and does not itself prove that a native
235
+ worker started.
236
+
237
+ The host adapter must keep an adaptive-runtime graph lease for the whole worker run, and an
238
+ executor lease when Fuli manages that executor. Call `acquire_runtime_lease` before starting
239
+ workers; its MCP process keeps the heartbeat alive. Call `release_runtime_lease` in a `finally`
240
+ path after all results and terminal events are recorded. Record queued/running and terminal state
241
+ with `record_project_agent_task_activity`,
242
+ including concrete worker identity. Call `record_project_agent_executor_actual` only after real
243
+ executor/model use. If the host cannot start a worker, report the task as blocked or failed and do
244
+ not fabricate an execution-summary row. The Provider's `executionSummary` remains authoritative.
245
+
246
+ ## Report Actual Worker Execution
247
+
248
+ When a FULI task or task view returns a non-empty Provider `executionSummary`, append one row per worker.
249
+ Include the worker label and occupation emoji, actual executor and `sourceApplication` environment,
250
+ work summary, and `workerStatus`/terminal status. This applies only to Provider-reported actual workers.
251
+ When empty `executionSummary` is returned, omit rows; an absent `executionSummary` also omits rows.
252
+ Configured, allowed, or available clients/executors are not evidence of actual use. Keep this
253
+ Agent-agnostic; client-specific details and work-kind routing remain at adapter boundaries.
254
+
216
255
  ## Classify Knowledge State
217
256
 
218
257
  Every entity and relationship needs an `originQuadrant`, `confirmationBasis`, and
@@ -254,8 +293,10 @@ confirmation time. Legacy items without that audit record are `pending`, even if
254
293
  for new product logic and do not treat them as confirmation evidence.
255
294
 
256
295
  Agent retrieval includes pending items on demand and returns their status so the caller can
257
- calibrate claims. Only user or authoritative-source `confirmed` project knowledge may enter
258
- public review, regardless of how it was originally discovered; normal personal preview and
296
+ calibrate claims. Only project knowledge whose immutable discovery-time `originQuadrant` is
297
+ `known_known` and whose status is user or authoritative-source `confirmed` may enter public
298
+ review. Knowledge first discovered as `known_unknown`, `unknown_known`, or `unknown_unknown`
299
+ remains in the personal graph even if it is later confirmed; normal personal preview and
259
300
  Maintainer review gates still apply.
260
301
 
261
302
  ## Grow The Personal Profile Separately
@@ -0,0 +1,127 @@
1
+ export function createExecutorPool({
2
+ adapters = new Map(),
3
+ idleMs,
4
+ setTimer = setTimeout,
5
+ clearTimer = clearTimeout
6
+ }) {
7
+ const entries = new Map();
8
+ let closed = false;
9
+
10
+ async function acquire(executorId, leaseId) {
11
+ assertExecutorId(executorId);
12
+ if (closed) throw new Error('Executor pool is closed');
13
+ const entry = entryFor(executorId);
14
+ if (entry.timer) {
15
+ clearTimer(entry.timer);
16
+ entry.timer = null;
17
+ }
18
+ entry.leases.add(leaseId);
19
+ if (!entry.adapter) {
20
+ entry.stage = 'external';
21
+ return snapshotEntry(executorId, entry);
22
+ }
23
+ await exclusive(entry, async () => {
24
+ if (entry.stage !== 'running') {
25
+ entry.stage = 'starting';
26
+ try {
27
+ await entry.adapter.start({ executorId });
28
+ entry.stage = 'running';
29
+ entry.lastError = null;
30
+ } catch (error) {
31
+ entry.leases.delete(leaseId);
32
+ entry.stage = 'failed';
33
+ entry.lastError = errorMessage(error);
34
+ throw error;
35
+ }
36
+ }
37
+ });
38
+ return snapshotEntry(executorId, entry);
39
+ }
40
+
41
+ function release(executorId, leaseId) {
42
+ const entry = entries.get(executorId);
43
+ if (!entry) return;
44
+ entry.leases.delete(leaseId);
45
+ if (!entry.adapter || entry.leases.size > 0 || entry.timer || closed) return;
46
+ entry.timer = setTimer(() => {
47
+ entry.timer = null;
48
+ if (entry.leases.size > 0 || closed) return;
49
+ void exclusive(entry, async () => {
50
+ if (entry.leases.size > 0 || closed) return;
51
+ await stopEntry(executorId, entry);
52
+ });
53
+ }, idleMs);
54
+ entry.timer?.unref?.();
55
+ }
56
+
57
+ async function stopEntry(executorId, entry) {
58
+ entry.stage = 'stopping';
59
+ try {
60
+ await entry.adapter.stop({ executorId });
61
+ entry.stage = 'idle';
62
+ entry.lastError = null;
63
+ } catch (error) {
64
+ entry.stage = 'failed';
65
+ entry.lastError = errorMessage(error);
66
+ }
67
+ }
68
+
69
+ function status() {
70
+ return [...entries.entries()].map(([executorId, entry]) =>
71
+ snapshotEntry(executorId, entry));
72
+ }
73
+
74
+ function close() {
75
+ closed = true;
76
+ for (const entry of entries.values()) {
77
+ if (entry.timer) clearTimer(entry.timer);
78
+ entry.timer = null;
79
+ }
80
+ }
81
+
82
+ function entryFor(executorId) {
83
+ let entry = entries.get(executorId);
84
+ if (!entry) {
85
+ entry = {
86
+ adapter: adapters.get(executorId) ?? null,
87
+ leases: new Set(),
88
+ timer: null,
89
+ transition: Promise.resolve(),
90
+ stage: adapters.has(executorId) ? 'idle' : 'external',
91
+ lastError: null
92
+ };
93
+ entries.set(executorId, entry);
94
+ }
95
+ return entry;
96
+ }
97
+
98
+ return { acquire, release, status, close };
99
+ }
100
+
101
+ function exclusive(entry, operation) {
102
+ const result = entry.transition.then(operation, operation);
103
+ entry.transition = result.catch(() => {});
104
+ return result;
105
+ }
106
+
107
+ function snapshotEntry(executorId, entry) {
108
+ return {
109
+ executorId,
110
+ managed: Boolean(entry.adapter),
111
+ stage: entry.stage,
112
+ activeLeaseCount: entry.leases.size,
113
+ lastError: entry.lastError
114
+ };
115
+ }
116
+
117
+ function assertExecutorId(value) {
118
+ if (typeof value !== 'string' || !value.trim() || value.length > 200) {
119
+ throw new TypeError('executorId must be non-empty text with at most 200 characters');
120
+ }
121
+ }
122
+
123
+ function errorMessage(error) {
124
+ return String(error instanceof Error ? error.message : error)
125
+ .replace(/[\r\n\t]+/g, ' ')
126
+ .slice(0, 500);
127
+ }
@@ -0,0 +1,166 @@
1
+ import { readJsonFile } from '../storage/json-file.js';
2
+ import { createNativeGraphServices } from '../native-runtime/runtime.js';
3
+ import {
4
+ ensureContainerRuntime,
5
+ runDockerCompose
6
+ } from '../setup/container-runtime.js';
7
+ import {
8
+ DEFAULT_RUNTIME_SETTINGS,
9
+ managedProviderUrls,
10
+ readRuntimeSettings
11
+ } from '../system/runtime-settings.js';
12
+
13
+ const PERSONAL_SERVICES = Object.freeze({
14
+ providers: ['personal-provider'],
15
+ databases: ['personal-neo4j']
16
+ });
17
+ const WORKSPACE_SERVICES = Object.freeze({
18
+ providers: ['workspace-provider'],
19
+ databases: ['workspace-neo4j']
20
+ });
21
+
22
+ export function createGraphServices({
23
+ paths,
24
+ readSettings = readRuntimeSettings,
25
+ createContainerServices = createManagedGraphServices,
26
+ createNativeServices = createNativeGraphServices
27
+ }) {
28
+ const settings = paths.runtimeSettingsPath
29
+ ? readSettings(paths.runtimeSettingsPath)
30
+ : DEFAULT_RUNTIME_SETTINGS;
31
+ return settings.graphRuntimeMode === 'native'
32
+ ? createNativeServices({ paths })
33
+ : createContainerServices({ paths });
34
+ }
35
+
36
+ export function createManagedGraphServices({
37
+ paths,
38
+ readJson = readJsonFile,
39
+ readSettings = readRuntimeSettings,
40
+ ensureRuntime = ensureContainerRuntime,
41
+ runCompose = runDockerCompose,
42
+ fetchImpl = globalThis.fetch,
43
+ wait = delay,
44
+ onProgress = () => {}
45
+ }) {
46
+ let containerRuntime = null;
47
+
48
+ async function runtime() {
49
+ if (containerRuntime?.status === 'ready') return containerRuntime;
50
+ containerRuntime = await ensureRuntime({ onProgress });
51
+ return containerRuntime;
52
+ }
53
+
54
+ function managedServices() {
55
+ const state = readJson(paths.graphRuntimeStatePath, null);
56
+ const config = readJson(paths.graphRuntimeConfigPath, null);
57
+ const workspace = state?.managedProviders?.includes('development-workspace') ||
58
+ (config?.workspaces ?? []).some(isManagedWorkspace);
59
+ return {
60
+ providers: [
61
+ ...PERSONAL_SERVICES.providers,
62
+ ...(workspace ? WORKSPACE_SERVICES.providers : [])
63
+ ],
64
+ databases: [
65
+ ...PERSONAL_SERVICES.databases,
66
+ ...(workspace ? WORKSPACE_SERVICES.databases : [])
67
+ ],
68
+ providerUrls: providerUrls(config, paths, readSettings, workspace)
69
+ };
70
+ }
71
+
72
+ async function start() {
73
+ const services = managedServices();
74
+ compose([
75
+ 'up', '-d', '--no-build',
76
+ ...services.databases,
77
+ ...services.providers
78
+ ], await runtime());
79
+ for (const url of services.providerUrls) await waitForProvider(url, fetchImpl, wait);
80
+ }
81
+
82
+ async function stopProviders() {
83
+ const services = managedServices();
84
+ compose(['stop', '-t', '20', ...services.providers], await runtime());
85
+ }
86
+
87
+ async function stopDatabases() {
88
+ const services = managedServices();
89
+ // Keep shutdown ordering explicit even if an external action restarted a Provider.
90
+ compose(['stop', '-t', '20', ...services.providers], await runtime());
91
+ compose(['stop', '-t', '20', ...services.databases], await runtime());
92
+ }
93
+
94
+ async function ready() {
95
+ const services = managedServices();
96
+ const checks = await Promise.all(services.providerUrls.map((url) =>
97
+ providerReady(url, fetchImpl)));
98
+ return checks.every(Boolean);
99
+ }
100
+
101
+ function compose(action, selectedRuntime) {
102
+ runCompose([
103
+ 'compose',
104
+ '--env-file', paths.graphEnvPath,
105
+ '-f', paths.graphComposePath,
106
+ ...action
107
+ ], selectedRuntime);
108
+ }
109
+
110
+ return { start, stopProviders, stopDatabases, ready };
111
+ }
112
+
113
+ function providerUrls(config, paths, readSettings, includesWorkspace) {
114
+ const settings = paths.runtimeSettingsPath
115
+ ? readSettings(paths.runtimeSettingsPath)
116
+ : DEFAULT_RUNTIME_SETTINGS;
117
+ const defaults = managedProviderUrls(settings);
118
+ const urls = [config?.personal?.providerUrl ?? defaults.personal];
119
+ if (!includesWorkspace) return urls;
120
+ const managed = (config?.workspaces ?? []).find(isManagedWorkspace);
121
+ return [...urls, managed?.providerUrl ?? defaults.workspace];
122
+ }
123
+
124
+ function isManagedWorkspace(workspace) {
125
+ if (workspace?.managedDevelopment === true) return true;
126
+ try {
127
+ const url = new URL(workspace?.providerUrl);
128
+ return url.protocol === 'http:' && isLoopback(url.hostname) && url.port === '8788';
129
+ } catch {
130
+ return false;
131
+ }
132
+ }
133
+
134
+ async function waitForProvider(url, fetchImpl, wait) {
135
+ for (let attempt = 0; attempt < 120; attempt += 1) {
136
+ try {
137
+ const response = await fetchImpl(`${String(url).replace(/\/$/, '')}/health`, {
138
+ signal: AbortSignal.timeout(2500)
139
+ });
140
+ if (response.ok) return;
141
+ } catch {
142
+ // Neo4j recovery and Provider index initialization can take a while after a cold wake.
143
+ }
144
+ if (attempt < 119) await wait(1000);
145
+ }
146
+ throw new Error('The managed graph Provider did not become ready after waking');
147
+ }
148
+
149
+ async function providerReady(url, fetchImpl) {
150
+ try {
151
+ const response = await fetchImpl(`${String(url).replace(/\/$/, '')}/health`, {
152
+ signal: AbortSignal.timeout(2500)
153
+ });
154
+ return response.ok;
155
+ } catch {
156
+ return false;
157
+ }
158
+ }
159
+
160
+ function isLoopback(hostname) {
161
+ return ['127.0.0.1', 'localhost', '::1', '[::1]'].includes(hostname);
162
+ }
163
+
164
+ function delay(ms) {
165
+ return new Promise((resolve) => setTimeout(resolve, ms));
166
+ }