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.
- package/README.md +77 -6
- package/README.zh-CN.md +69 -6
- 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-D_2zC4lm.js → console-BlAhujsH.js} +1 -1
- package/dist/web/assets/{conversation-launcher-settings-DQmMH2j7.js → conversation-launcher-settings-DTYxiLJt.js} +1 -1
- package/dist/web/assets/i18n-Ci3heuiL.js +9 -0
- package/dist/web/assets/{identity-DmGcY3Y6.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-CVoOdRsL.js → model-yG0YqUyZ.js} +1 -1
- package/dist/web/assets/{useApi-CROJJdhE-CBNg4CdH.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/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 +130 -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 +1 -1
- package/skills/capturing-session-knowledge/SKILL.md +39 -0
- 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 +6 -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.js +6 -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 +53 -90
- 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/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-Dpf5fRWv.js +0 -1
- package/dist/web/assets/ConnectionsPage-D21aUDCP.js +0 -1
- package/dist/web/assets/KnowledgeEditDialog-u7lpfN3j.js +0 -1
- package/dist/web/assets/KnowledgeInspector-k4xnHtXd.js +0 -1
- package/dist/web/assets/KnowledgeOrganizerPage-Bdj7Rn84.js +0 -1
- package/dist/web/assets/KnowledgePage-UHVAbC1W.js +0 -1
- package/dist/web/assets/KnowledgeWorkspace-Lq3d-MeC.js +0 -4
- package/dist/web/assets/OverviewPage-CycAgA1V.js +0 -1
- package/dist/web/assets/PersonalProfilePage-U9oIV5FT.js +0 -1
- package/dist/web/assets/PersonalProjectsPage-C5RpXKcA.js +0 -1
- package/dist/web/assets/PublicProjectsPage-DiWxm35E.js +0 -1
- package/dist/web/assets/ReviewPage-jxRfGSJK.js +0 -1
- package/dist/web/assets/SearchableSelect-B3-5IPup.js +0 -1
- package/dist/web/assets/SettingsPage-BqfGDOho.js +0 -1
- package/dist/web/assets/SettingsPage-CAfz4qiV.css +0 -1
- package/dist/web/assets/WritingTastePage-BJxhMqJR.js +0 -1
- package/dist/web/assets/i18n-CwqXDI1C.js +0 -5
- package/dist/web/assets/index-IUGLt4lG.js +0 -2
- package/dist/web/assets/index-X2j29j8i.css +0 -1
- package/dist/web/assets/vue-router-CWSsUUio.js +0 -1
package/src/server.js
CHANGED
|
@@ -63,7 +63,8 @@ export async function createServer(options = {}) {
|
|
|
63
63
|
}),
|
|
64
64
|
packageRoot: PACKAGE_ROOT,
|
|
65
65
|
activePort: port,
|
|
66
|
-
activeLan: lanEnabled
|
|
66
|
+
activeLan: lanEnabled,
|
|
67
|
+
executorAdapters: options.executorAdapters
|
|
67
68
|
})
|
|
68
69
|
: null);
|
|
69
70
|
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
@@ -109,7 +110,10 @@ export async function createServer(options = {}) {
|
|
|
109
110
|
throw new Error('Server did not bind to a valid local authority');
|
|
110
111
|
}
|
|
111
112
|
|
|
112
|
-
server.once('close',
|
|
113
|
+
server.once('close', () => {
|
|
114
|
+
system?.close?.();
|
|
115
|
+
runtime.close();
|
|
116
|
+
});
|
|
113
117
|
return {
|
|
114
118
|
server,
|
|
115
119
|
url: `http://${authority}`,
|
|
@@ -63,7 +63,13 @@ export function inspectAgentInstallations(agents, context, {
|
|
|
63
63
|
function inspectRegistration(agent, context, readers) {
|
|
64
64
|
const expected = {
|
|
65
65
|
command: context.nodePath,
|
|
66
|
-
args: [
|
|
66
|
+
args: [
|
|
67
|
+
context.mcpServerPath,
|
|
68
|
+
'--runtime-config',
|
|
69
|
+
context.runtimeConfigPath,
|
|
70
|
+
'--source-application',
|
|
71
|
+
agent.id === 'claude-code' ? 'claude_code' : agent.id
|
|
72
|
+
]
|
|
67
73
|
};
|
|
68
74
|
try {
|
|
69
75
|
if (agent.id === 'codex') {
|
package/src/setup/agents.js
CHANGED
|
@@ -60,7 +60,9 @@ export function buildAgentCommands(agent, context) {
|
|
|
60
60
|
const serverArgs = [
|
|
61
61
|
context.mcpServerPath,
|
|
62
62
|
'--runtime-config',
|
|
63
|
-
context.runtimeConfigPath
|
|
63
|
+
context.runtimeConfigPath,
|
|
64
|
+
'--source-application',
|
|
65
|
+
agent.id === 'claude-code' ? 'claude_code' : agent.id
|
|
64
66
|
];
|
|
65
67
|
const mcpArgs = [context.nodePath, ...serverArgs];
|
|
66
68
|
if (agent.id === 'codex') {
|
|
@@ -15,16 +15,17 @@ const BLOCK_START = '<!-- BEGIN FULI MANAGED INSTRUCTIONS -->';
|
|
|
15
15
|
const BLOCK_END = '<!-- END FULI MANAGED INSTRUCTIONS -->';
|
|
16
16
|
|
|
17
17
|
const GLOBAL_INSTRUCTIONS = `${BLOCK_START}
|
|
18
|
-
At
|
|
19
|
-
\`get_collaboration_preferences\` with \`projectPath\`=current working directory
|
|
20
|
-
\`taskPrompt\`=current user request
|
|
21
|
-
|
|
22
|
-
Before asking
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
At start of every user task, before any other tool or answer, call exactly
|
|
19
|
+
\`get_collaboration_preferences\` with \`projectPath\`=current working directory,
|
|
20
|
+
\`taskPrompt\`=current user request. Never substitute a project action/Fuli tool.
|
|
21
|
+
Fuli never stores or returns them; inputs are transient. Apply all returned \`effective_preferences\`.
|
|
22
|
+
Before asking user to repeat a stable project fact or method, inspect \`task_knowledge_recall\`.
|
|
23
|
+
On miss call \`search_current_project_knowledge\` with focused action/artifact/target/identifier
|
|
24
|
+
queries; never use the full request as the only query.
|
|
25
|
+
Write tools enforce preferences in actual payload; mentioning later is not compliance.
|
|
26
|
+
Resolve relevant \`deferred_conflict\` first; ignore others.
|
|
27
|
+
Team: \`coordinate_project_agent_task\`; host \`acquire_runtime_lease\`, start workers, report
|
|
28
|
+
execution, \`release_runtime_lease\` in finally.
|
|
28
29
|
${codexReviewCommandBridge()}
|
|
29
30
|
${BLOCK_END}`;
|
|
30
31
|
|
|
@@ -22,7 +22,13 @@ export function connectCodex(agent, context, {
|
|
|
22
22
|
const current = readConfig(agent.configPath);
|
|
23
23
|
const next = replaceFuliTable(current, {
|
|
24
24
|
command: context.nodePath,
|
|
25
|
-
args: [
|
|
25
|
+
args: [
|
|
26
|
+
context.mcpServerPath,
|
|
27
|
+
'--runtime-config',
|
|
28
|
+
context.runtimeConfigPath,
|
|
29
|
+
'--source-application',
|
|
30
|
+
'codex'
|
|
31
|
+
]
|
|
26
32
|
});
|
|
27
33
|
const bootstrap = installBootstrap(agent, context);
|
|
28
34
|
const registrationChanged = next !== current;
|
|
@@ -15,6 +15,10 @@ import {
|
|
|
15
15
|
ensureContainerRuntime,
|
|
16
16
|
runDockerCompose
|
|
17
17
|
} from './container-runtime.js';
|
|
18
|
+
import {
|
|
19
|
+
createNativeGraphServices,
|
|
20
|
+
ensureNativeRuntime
|
|
21
|
+
} from '../native-runtime/runtime.js';
|
|
18
22
|
import {
|
|
19
23
|
discoverLanAddresses,
|
|
20
24
|
LAN_ACCESS_USERNAME,
|
|
@@ -28,6 +32,20 @@ import {
|
|
|
28
32
|
runtimeSettingsWithOverrides,
|
|
29
33
|
writeRuntimeSettings
|
|
30
34
|
} from '../system/runtime-settings.js';
|
|
35
|
+
import {
|
|
36
|
+
neo4jMemoryEnvironment,
|
|
37
|
+
resolveNeo4jMemoryProfile
|
|
38
|
+
} from './neo4j-memory-profile.js';
|
|
39
|
+
import {
|
|
40
|
+
DEFAULT_ADAPTIVE_RUNTIME_SETTINGS,
|
|
41
|
+
normalizeAdaptiveRuntimeSettings,
|
|
42
|
+
readAdaptiveRuntimeSettings,
|
|
43
|
+
writeAdaptiveRuntimeSettings
|
|
44
|
+
} from '../adaptive-runtime/settings.js';
|
|
45
|
+
import {
|
|
46
|
+
initialAdaptiveRuntimeState,
|
|
47
|
+
writeAdaptiveRuntimeState
|
|
48
|
+
} from '../adaptive-runtime/state.js';
|
|
31
49
|
|
|
32
50
|
export {
|
|
33
51
|
dockerInfoIndicatesDaemon,
|
|
@@ -42,31 +60,79 @@ export async function ensureGraphRuntime(input, dependencies = {}) {
|
|
|
42
60
|
lanAccess: input.lan === true
|
|
43
61
|
}));
|
|
44
62
|
const urls = managedProviderUrls(runtimeSettings);
|
|
63
|
+
const adaptiveRuntimeSettings = normalizeAdaptiveRuntimeSettings(
|
|
64
|
+
input.adaptiveRuntimeSettings ?? (input.paths.adaptiveRuntimeSettingsPath
|
|
65
|
+
? deps.readAdaptiveSettings(input.paths.adaptiveRuntimeSettingsPath)
|
|
66
|
+
: DEFAULT_ADAPTIVE_RUNTIME_SETTINGS)
|
|
67
|
+
);
|
|
45
68
|
const lan = runtimeSettings.lanAccess;
|
|
69
|
+
const runtimeMode = input.runtimeMode ?? runtimeSettings.graphRuntimeMode;
|
|
70
|
+
const activeRuntimeState = deps.readState(input.paths.graphRuntimeStatePath);
|
|
71
|
+
const previousRuntimeMode = input.previousRuntimeMode ??
|
|
72
|
+
activeRuntimeState?.runtimeSettings?.graphRuntimeMode ?? runtimeMode;
|
|
46
73
|
const lanAddresses = lan ? deps.discoverLanAddresses() : [];
|
|
47
74
|
if (lan && lanAddresses.length === 0) {
|
|
48
75
|
throw new Error('No private IPv4 LAN address is available, so LAN mode cannot start.');
|
|
49
76
|
}
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
77
|
+
const selectedGraphRuntime = runtimeMode === 'native'
|
|
78
|
+
? await deps.ensureNativeRuntime({
|
|
79
|
+
paths: input.paths,
|
|
80
|
+
env: input.env ?? process.env,
|
|
81
|
+
runtimeSettings,
|
|
82
|
+
memoryProfile: input.memoryProfile,
|
|
83
|
+
personalOnly: input.personalOnly === true,
|
|
84
|
+
onProgress: input.onProgress
|
|
85
|
+
})
|
|
86
|
+
: await deps.ensureContainerRuntime({
|
|
87
|
+
env: input.env ?? process.env,
|
|
88
|
+
onProgress: input.onProgress
|
|
89
|
+
});
|
|
54
90
|
deps.ensureDirectory(input.paths.dataDir);
|
|
91
|
+
const configSelection = selectRuntimeConfig({
|
|
92
|
+
paths: input.paths,
|
|
93
|
+
previousRuntimeMode,
|
|
94
|
+
runtimeMode,
|
|
95
|
+
deps
|
|
96
|
+
});
|
|
97
|
+
if (input.paths.adaptiveRuntimeSettingsPath) {
|
|
98
|
+
deps.writeAdaptiveSettings(
|
|
99
|
+
input.paths.adaptiveRuntimeSettingsPath,
|
|
100
|
+
adaptiveRuntimeSettings
|
|
101
|
+
);
|
|
102
|
+
}
|
|
55
103
|
const secrets = ensureProviderEnvironment(
|
|
56
104
|
input.paths.graphEnvPath,
|
|
57
105
|
runtimeSettings,
|
|
106
|
+
input.memoryProfile,
|
|
58
107
|
deps
|
|
59
108
|
);
|
|
60
109
|
deps.writeRuntimeSettings(input.paths.runtimeSettingsPath, runtimeSettings);
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
110
|
+
if (runtimeMode === 'native') {
|
|
111
|
+
await deps.startNativeProviders(input.paths, input.paths.graphEnvPath, {
|
|
112
|
+
personalOnly: input.personalOnly === true,
|
|
113
|
+
nativeRuntime: selectedGraphRuntime
|
|
114
|
+
});
|
|
115
|
+
} else {
|
|
116
|
+
deps.startProviders(input.paths, input.paths.graphEnvPath, {
|
|
117
|
+
personalOnly: input.personalOnly === true,
|
|
118
|
+
containerRuntime: selectedGraphRuntime,
|
|
119
|
+
...(input.buildProviders === false ? { build: false } : {})
|
|
120
|
+
});
|
|
121
|
+
}
|
|
66
122
|
await waitForProvider(urls.personal, deps.fetch);
|
|
67
123
|
if (!input.personalOnly) await waitForProvider(urls.workspace, deps.fetch);
|
|
124
|
+
if (input.paths.adaptiveRuntimeStatePath) {
|
|
125
|
+
deps.writeAdaptiveState(
|
|
126
|
+
input.paths.adaptiveRuntimeStatePath,
|
|
127
|
+
initialAdaptiveRuntimeState({ enabled: adaptiveRuntimeSettings.enabled })
|
|
128
|
+
);
|
|
129
|
+
}
|
|
68
130
|
|
|
69
|
-
let config =
|
|
131
|
+
let config = configSelection.config;
|
|
132
|
+
if (configSelection.switched && config) {
|
|
133
|
+
deps.writeConfig(input.paths.graphRuntimeConfigPath, config);
|
|
134
|
+
deps.secureFile(input.paths.graphRuntimeConfigPath);
|
|
135
|
+
}
|
|
70
136
|
if (!config) {
|
|
71
137
|
config = await bootstrapGraph({
|
|
72
138
|
urls,
|
|
@@ -95,9 +161,16 @@ export async function ensureGraphRuntime(input, dependencies = {}) {
|
|
|
95
161
|
deps.secureFile(input.paths.graphRuntimeConfigPath);
|
|
96
162
|
}
|
|
97
163
|
}
|
|
164
|
+
const activeProfilePath = configSelection.switched
|
|
165
|
+
? graphConfigProfilePath(input.paths, runtimeMode)
|
|
166
|
+
: null;
|
|
167
|
+
if (activeProfilePath) {
|
|
168
|
+
deps.writeConfig(activeProfilePath, config);
|
|
169
|
+
deps.secureFile(activeProfilePath);
|
|
170
|
+
}
|
|
98
171
|
|
|
99
172
|
if (input.noStart) return { status: 'initialized', url: null, pid: null };
|
|
100
|
-
const existing =
|
|
173
|
+
const existing = activeRuntimeState;
|
|
101
174
|
if (isGraphRuntimeState(existing) && deps.isProcessAlive(existing.pid)) {
|
|
102
175
|
const healthy = await deps.webHealth(existing.url, existing.pid, existing.version);
|
|
103
176
|
if (
|
|
@@ -105,6 +178,9 @@ export async function ensureGraphRuntime(input, dependencies = {}) {
|
|
|
105
178
|
existing.port === runtimeSettings.ports.console &&
|
|
106
179
|
exposureMatches(existing, { lan, lanAddresses })
|
|
107
180
|
) {
|
|
181
|
+
if (adaptiveRuntimeSettings.enabled) {
|
|
182
|
+
await notifyAdaptiveRuntime(existing.url, deps.fetch);
|
|
183
|
+
}
|
|
108
184
|
return runtimeResult('running', existing);
|
|
109
185
|
}
|
|
110
186
|
if (!healthy) throw new Error('Recorded Fuli Graphiti runtime is not healthy');
|
|
@@ -149,10 +225,38 @@ export async function ensureGraphRuntime(input, dependencies = {}) {
|
|
|
149
225
|
return runtimeResult('started', state, lanAccessToken);
|
|
150
226
|
}
|
|
151
227
|
|
|
152
|
-
function
|
|
228
|
+
function selectRuntimeConfig({ paths, previousRuntimeMode, runtimeMode, deps }) {
|
|
229
|
+
const current = deps.readConfig(paths.graphRuntimeConfigPath);
|
|
230
|
+
const switched = previousRuntimeMode !== runtimeMode;
|
|
231
|
+
if (!switched) return { switched: false, config: current };
|
|
232
|
+
|
|
233
|
+
const previousProfilePath = graphConfigProfilePath(paths, previousRuntimeMode);
|
|
234
|
+
if (current && previousProfilePath) {
|
|
235
|
+
deps.writeConfig(previousProfilePath, current);
|
|
236
|
+
deps.secureFile(previousProfilePath);
|
|
237
|
+
}
|
|
238
|
+
const targetProfilePath = graphConfigProfilePath(paths, runtimeMode);
|
|
239
|
+
return {
|
|
240
|
+
switched: true,
|
|
241
|
+
config: targetProfilePath ? deps.readConfig(targetProfilePath) : null
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function graphConfigProfilePath(paths, mode) {
|
|
246
|
+
if (mode === 'native') return paths.nativeGraphConfigProfilePath ?? null;
|
|
247
|
+
if (mode === 'container') return paths.containerGraphConfigProfilePath ?? null;
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function ensureProviderEnvironment(path, settings, requestedMemoryProfile, deps) {
|
|
153
252
|
const previous = deps.fileExists(path) ? parseEnv(deps.readText(path)) : {};
|
|
253
|
+
const memoryProfile = resolveNeo4jMemoryProfile(
|
|
254
|
+
requestedMemoryProfile,
|
|
255
|
+
previous.FULI_NEO4J_MEMORY_PROFILE
|
|
256
|
+
);
|
|
154
257
|
const values = {
|
|
155
258
|
...previous,
|
|
259
|
+
...neo4jMemoryEnvironment(memoryProfile),
|
|
156
260
|
FULI_PERSONAL_NEO4J_PASSWORD: previous.FULI_PERSONAL_NEO4J_PASSWORD ?? secret(),
|
|
157
261
|
FULI_WORKSPACE_NEO4J_PASSWORD: previous.FULI_WORKSPACE_NEO4J_PASSWORD ?? secret(),
|
|
158
262
|
FULI_PERSONAL_BOOTSTRAP_TOKEN: previous.FULI_PERSONAL_BOOTSTRAP_TOKEN ?? secret(),
|
|
@@ -390,7 +494,14 @@ export function startGraphProviders(paths, envPath, options = {}) {
|
|
|
390
494
|
runDockerCompose(args, options.containerRuntime);
|
|
391
495
|
}
|
|
392
496
|
|
|
393
|
-
export function stopGraphProviders(paths, envPath, options = {}) {
|
|
497
|
+
export async function stopGraphProviders(paths, envPath, options = {}) {
|
|
498
|
+
if (options.runtimeMode === 'native') {
|
|
499
|
+
await createNativeGraphServices({
|
|
500
|
+
paths,
|
|
501
|
+
personalOnly: options.personalOnly === true
|
|
502
|
+
}).stopDatabases();
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
394
505
|
const args = [
|
|
395
506
|
'compose',
|
|
396
507
|
'--env-file', envPath,
|
|
@@ -437,10 +548,19 @@ function graphDependencies(overrides) {
|
|
|
437
548
|
readConfig: (path) => readJsonFile(path, null),
|
|
438
549
|
writeConfig: writeJsonFileAtomic,
|
|
439
550
|
writeRuntimeSettings,
|
|
551
|
+
readAdaptiveSettings: readAdaptiveRuntimeSettings,
|
|
552
|
+
writeAdaptiveSettings: writeAdaptiveRuntimeSettings,
|
|
553
|
+
writeAdaptiveState: writeAdaptiveRuntimeState,
|
|
440
554
|
readState: (path) => readJsonFile(path, null),
|
|
441
555
|
writeState: writeJsonFileAtomic,
|
|
442
556
|
ensureContainerRuntime,
|
|
557
|
+
ensureNativeRuntime,
|
|
443
558
|
startProviders: startGraphProviders,
|
|
559
|
+
startNativeProviders: (paths, _envPath, options) => createNativeGraphServices({
|
|
560
|
+
paths,
|
|
561
|
+
runtimeDescriptor: options.nativeRuntime,
|
|
562
|
+
personalOnly: options.personalOnly
|
|
563
|
+
}).start(),
|
|
444
564
|
spawnWebRuntime,
|
|
445
565
|
isProcessAlive,
|
|
446
566
|
stopProcess,
|
|
@@ -452,6 +572,31 @@ function graphDependencies(overrides) {
|
|
|
452
572
|
};
|
|
453
573
|
}
|
|
454
574
|
|
|
575
|
+
async function notifyAdaptiveRuntime(url, fetchImpl) {
|
|
576
|
+
let leaseId = null;
|
|
577
|
+
try {
|
|
578
|
+
const acquired = await fetchImpl(`${url}/api/system/runtime/leases`, {
|
|
579
|
+
method: 'POST',
|
|
580
|
+
headers: { 'content-type': 'application/json' },
|
|
581
|
+
body: JSON.stringify({ kind: 'graph', owner: 'setup' }),
|
|
582
|
+
signal: AbortSignal.timeout(180_000)
|
|
583
|
+
});
|
|
584
|
+
if (!acquired.ok) return;
|
|
585
|
+
leaseId = (await acquired.json())?.leaseId ?? null;
|
|
586
|
+
} catch {
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
if (!leaseId) return;
|
|
590
|
+
try {
|
|
591
|
+
await fetchImpl(`${url}/api/system/runtime/leases/${encodeURIComponent(leaseId)}`, {
|
|
592
|
+
method: 'DELETE',
|
|
593
|
+
signal: AbortSignal.timeout(10_000)
|
|
594
|
+
});
|
|
595
|
+
} catch {
|
|
596
|
+
// Lease expiry remains the safety net if setup cannot release explicitly.
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
455
600
|
export async function checkLocalConsoleHealth(url, expectedPid, stateVersion = 3) {
|
|
456
601
|
try {
|
|
457
602
|
const response = await fetch(`${url}/api/health`, {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const DEFAULT_NEO4J_MEMORY_PROFILE = 'balanced';
|
|
2
|
+
|
|
3
|
+
export const NEO4J_MEMORY_PROFILES = Object.freeze({
|
|
4
|
+
balanced: Object.freeze({
|
|
5
|
+
heapInitial: '256m',
|
|
6
|
+
heapMax: '512m',
|
|
7
|
+
pageCache: '256m'
|
|
8
|
+
}),
|
|
9
|
+
low: Object.freeze({
|
|
10
|
+
heapInitial: '128m',
|
|
11
|
+
heapMax: '256m',
|
|
12
|
+
pageCache: '64m'
|
|
13
|
+
})
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export function normalizeNeo4jMemoryProfile(value) {
|
|
17
|
+
const profile = typeof value === 'string' ? value.trim() : '';
|
|
18
|
+
if (!Object.hasOwn(NEO4J_MEMORY_PROFILES, profile)) {
|
|
19
|
+
throw new TypeError('Neo4j memory profile must be "low" or "balanced"');
|
|
20
|
+
}
|
|
21
|
+
return profile;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function resolveNeo4jMemoryProfile(requested, saved) {
|
|
25
|
+
if (requested !== null && requested !== undefined) {
|
|
26
|
+
return normalizeNeo4jMemoryProfile(requested);
|
|
27
|
+
}
|
|
28
|
+
if (typeof saved === 'string' && saved.trim()) {
|
|
29
|
+
return normalizeNeo4jMemoryProfile(saved);
|
|
30
|
+
}
|
|
31
|
+
return DEFAULT_NEO4J_MEMORY_PROFILE;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function neo4jMemoryEnvironment(profile = DEFAULT_NEO4J_MEMORY_PROFILE) {
|
|
35
|
+
const resolved = normalizeNeo4jMemoryProfile(profile);
|
|
36
|
+
const values = NEO4J_MEMORY_PROFILES[resolved];
|
|
37
|
+
return {
|
|
38
|
+
FULI_NEO4J_MEMORY_PROFILE: resolved,
|
|
39
|
+
FULI_NEO4J_HEAP_INITIAL_SIZE: values.heapInitial,
|
|
40
|
+
FULI_NEO4J_HEAP_MAX_SIZE: values.heapMax,
|
|
41
|
+
FULI_NEO4J_PAGECACHE_SIZE: values.pageCache
|
|
42
|
+
};
|
|
43
|
+
}
|
package/src/setup/options.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import { normalizeNeo4jMemoryProfile } from './neo4j-memory-profile.js';
|
|
2
|
+
|
|
1
3
|
const VALUE_OPTIONS = Object.freeze({
|
|
2
4
|
'--data-dir': 'dataDir',
|
|
3
5
|
'--personal-space': 'personalSpaceName',
|
|
4
|
-
'--port': 'port'
|
|
6
|
+
'--port': 'port',
|
|
7
|
+
'--memory-profile': 'memoryProfile',
|
|
8
|
+
'--runtime-mode': 'runtimeMode'
|
|
5
9
|
});
|
|
6
10
|
|
|
7
11
|
const BOOLEAN_OPTIONS = Object.freeze({
|
|
@@ -11,6 +15,11 @@ const BOOLEAN_OPTIONS = Object.freeze({
|
|
|
11
15
|
'--no-start': 'noStart'
|
|
12
16
|
});
|
|
13
17
|
|
|
18
|
+
const ADAPTIVE_MEMORY_OPTIONS = Object.freeze({
|
|
19
|
+
'--adaptive-memory': true,
|
|
20
|
+
'--no-adaptive-memory': false
|
|
21
|
+
});
|
|
22
|
+
|
|
14
23
|
const PROVIDER_MODE_OPTIONS = new Set(['--personal-only', '--with-dev-public']);
|
|
15
24
|
|
|
16
25
|
export function parseSetupOptions(args = []) {
|
|
@@ -26,6 +35,9 @@ function parseSetupLikeOptions(args, command) {
|
|
|
26
35
|
dataDir: null,
|
|
27
36
|
personalSpaceName: 'Personal',
|
|
28
37
|
port: null,
|
|
38
|
+
memoryProfile: null,
|
|
39
|
+
runtimeMode: null,
|
|
40
|
+
adaptiveMemory: null,
|
|
29
41
|
yes: false,
|
|
30
42
|
codexOnly: false,
|
|
31
43
|
skipAgents: false,
|
|
@@ -37,6 +49,17 @@ function parseSetupLikeOptions(args, command) {
|
|
|
37
49
|
for (let index = 0; index < args.length; index += 1) {
|
|
38
50
|
const flag = args[index];
|
|
39
51
|
if (seen.has(flag)) throw new TypeError(`Duplicate ${flag}`);
|
|
52
|
+
if (Object.hasOwn(ADAPTIVE_MEMORY_OPTIONS, flag)) {
|
|
53
|
+
const conflicting = flag === '--adaptive-memory'
|
|
54
|
+
? '--no-adaptive-memory'
|
|
55
|
+
: '--adaptive-memory';
|
|
56
|
+
if (seen.has(conflicting)) {
|
|
57
|
+
throw new TypeError('--adaptive-memory and --no-adaptive-memory cannot be combined');
|
|
58
|
+
}
|
|
59
|
+
result.adaptiveMemory = ADAPTIVE_MEMORY_OPTIONS[flag];
|
|
60
|
+
seen.add(flag);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
40
63
|
if (PROVIDER_MODE_OPTIONS.has(flag)) {
|
|
41
64
|
const conflicting = flag === '--personal-only' ? '--with-dev-public' : '--personal-only';
|
|
42
65
|
if (seen.has(conflicting)) {
|
|
@@ -58,7 +81,7 @@ function parseSetupLikeOptions(args, command) {
|
|
|
58
81
|
if (typeof value !== 'string' || !value.trim() || value.startsWith('--')) {
|
|
59
82
|
throw new TypeError(`Missing value for ${flag}`);
|
|
60
83
|
}
|
|
61
|
-
result[valueKey] = valueKey
|
|
84
|
+
result[valueKey] = parseValue(valueKey, value);
|
|
62
85
|
seen.add(flag);
|
|
63
86
|
index += 1;
|
|
64
87
|
}
|
|
@@ -95,3 +118,16 @@ function parsePort(value) {
|
|
|
95
118
|
}
|
|
96
119
|
return port;
|
|
97
120
|
}
|
|
121
|
+
|
|
122
|
+
function parseValue(key, value) {
|
|
123
|
+
if (key === 'port') return parsePort(value);
|
|
124
|
+
if (key === 'memoryProfile') return normalizeNeo4jMemoryProfile(value);
|
|
125
|
+
if (key === 'runtimeMode') {
|
|
126
|
+
const mode = value.trim();
|
|
127
|
+
if (!['container', 'native'].includes(mode)) {
|
|
128
|
+
throw new TypeError('Runtime mode must be "container" or "native"');
|
|
129
|
+
}
|
|
130
|
+
return mode;
|
|
131
|
+
}
|
|
132
|
+
return value.trim();
|
|
133
|
+
}
|
package/src/setup/paths.js
CHANGED
|
@@ -14,12 +14,25 @@ export function resolveSetupPaths({
|
|
|
14
14
|
} = {}) {
|
|
15
15
|
const resolvedDataDir = resolve(cwd, dataDir ?? defaultDataDir({ platform, env, homeDir }));
|
|
16
16
|
const root = resolve(packageRoot);
|
|
17
|
+
const nativeRuntimeDir = join(resolvedDataDir, 'native-runtime');
|
|
18
|
+
const runtimeConfigDir = join(resolvedDataDir, 'runtime-configs');
|
|
17
19
|
return {
|
|
18
20
|
dataDir: resolvedDataDir,
|
|
19
21
|
graphEnvPath: join(resolvedDataDir, 'graph-provider.env'),
|
|
20
22
|
graphRuntimeConfigPath: join(resolvedDataDir, 'graph-runtime.json'),
|
|
21
23
|
graphRuntimeStatePath: join(resolvedDataDir, 'graph-runtime-state.json'),
|
|
24
|
+
adaptiveRuntimeSettingsPath: join(resolvedDataDir, 'adaptive-runtime-settings.json'),
|
|
25
|
+
adaptiveRuntimeStatePath: join(resolvedDataDir, 'adaptive-runtime-state.json'),
|
|
22
26
|
runtimeSettingsPath: join(resolvedDataDir, 'runtime-settings.json'),
|
|
27
|
+
nativeRuntimeDir,
|
|
28
|
+
nativeRuntimeManifestPath: join(nativeRuntimeDir, 'manifest.json'),
|
|
29
|
+
nativeProcessStatePath: join(nativeRuntimeDir, 'processes.json'),
|
|
30
|
+
nativeNeo4jHome: join(nativeRuntimeDir, 'neo4j-community-5.26.28'),
|
|
31
|
+
nativeProviderVenvPath: join(nativeRuntimeDir, 'provider-venv'),
|
|
32
|
+
nativePersonalDir: join(nativeRuntimeDir, 'personal'),
|
|
33
|
+
nativeWorkspaceDir: join(nativeRuntimeDir, 'workspace'),
|
|
34
|
+
containerGraphConfigProfilePath: join(runtimeConfigDir, 'container.json'),
|
|
35
|
+
nativeGraphConfigProfilePath: join(runtimeConfigDir, 'native.json'),
|
|
23
36
|
externalKnowledgeRegistryPath: join(
|
|
24
37
|
resolvedDataDir,
|
|
25
38
|
'external-knowledge',
|
|
@@ -36,6 +49,7 @@ export function resolveSetupPaths({
|
|
|
36
49
|
'connectors'
|
|
37
50
|
),
|
|
38
51
|
backupDir: join(resolvedDataDir, 'backups', 'agents'),
|
|
52
|
+
graphBackupDir: join(resolvedDataDir, 'backups', 'graph'),
|
|
39
53
|
logPath: join(resolvedDataDir, 'logs', 'runtime.log'),
|
|
40
54
|
serverPath: join(root, 'src', 'server.js'),
|
|
41
55
|
mcpServerPath: join(root, 'src', 'mcp-server.js'),
|
package/src/setup/setup.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { backupAgentConfig } from './config-backup.js';
|
|
2
|
+
import { totalmem } from 'node:os';
|
|
2
3
|
import { inspectAgentInstallations } from './agent-installation-status.js';
|
|
3
4
|
import { connectAgent, discoverAgents } from './agents.js';
|
|
4
5
|
import { resolveSetupPaths } from './paths.js';
|
|
@@ -9,6 +10,11 @@ import {
|
|
|
9
10
|
readRuntimeSettings,
|
|
10
11
|
runtimeSettingsWithOverrides
|
|
11
12
|
} from '../system/runtime-settings.js';
|
|
13
|
+
import {
|
|
14
|
+
DEFAULT_ADAPTIVE_RUNTIME_SETTINGS,
|
|
15
|
+
adaptiveRuntimeSettingsWithOverrides,
|
|
16
|
+
readAdaptiveRuntimeSettings
|
|
17
|
+
} from '../adaptive-runtime/settings.js';
|
|
12
18
|
|
|
13
19
|
export function planLocalSetup(options, {
|
|
14
20
|
env = process.env,
|
|
@@ -16,6 +22,9 @@ export function planLocalSetup(options, {
|
|
|
16
22
|
discover = discoverAgents,
|
|
17
23
|
inspectInstallations = inspectAgentInstallations,
|
|
18
24
|
readSettings = readRuntimeSettings,
|
|
25
|
+
readAdaptiveSettings = readAdaptiveRuntimeSettings,
|
|
26
|
+
platform = process.platform,
|
|
27
|
+
hostTotalMemory = totalmem,
|
|
19
28
|
nodePath = process.execPath
|
|
20
29
|
} = {}) {
|
|
21
30
|
const paths = resolvePaths({ dataDir: options.dataDir, env });
|
|
@@ -23,8 +32,16 @@ export function planLocalSetup(options, {
|
|
|
23
32
|
? readSettings(paths.runtimeSettingsPath)
|
|
24
33
|
: DEFAULT_RUNTIME_SETTINGS;
|
|
25
34
|
const runtimeSettings = runtimeSettingsWithOverrides(savedRuntimeSettings, {
|
|
26
|
-
consolePort: options.port
|
|
35
|
+
consolePort: options.port,
|
|
36
|
+
graphRuntimeMode: options.runtimeMode
|
|
27
37
|
});
|
|
38
|
+
const savedAdaptiveRuntimeSettings = paths.adaptiveRuntimeSettingsPath
|
|
39
|
+
? readAdaptiveSettings(paths.adaptiveRuntimeSettingsPath)
|
|
40
|
+
: DEFAULT_ADAPTIVE_RUNTIME_SETTINGS;
|
|
41
|
+
const adaptiveRuntimeSettings = adaptiveRuntimeSettingsWithOverrides(
|
|
42
|
+
savedAdaptiveRuntimeSettings,
|
|
43
|
+
{ enabled: options.adaptiveMemory ?? null }
|
|
44
|
+
);
|
|
28
45
|
const discovered = options.skipAgents ? [] : discover({ env });
|
|
29
46
|
const agents = options.codexOnly
|
|
30
47
|
? discovered.filter(({ id }) => id === 'codex')
|
|
@@ -32,6 +49,11 @@ export function planLocalSetup(options, {
|
|
|
32
49
|
return {
|
|
33
50
|
paths,
|
|
34
51
|
runtimeSettings,
|
|
52
|
+
runtimeModeRecommendation: graphRuntimeModeRecommendation({
|
|
53
|
+
platform,
|
|
54
|
+
hostTotalBytes: hostTotalMemory()
|
|
55
|
+
}),
|
|
56
|
+
adaptiveRuntimeSettings,
|
|
35
57
|
agents: inspectInstallations(agents, {
|
|
36
58
|
nodePath,
|
|
37
59
|
mcpServerPath: paths.mcpServerPath,
|
|
@@ -43,6 +65,17 @@ export function planLocalSetup(options, {
|
|
|
43
65
|
};
|
|
44
66
|
}
|
|
45
67
|
|
|
68
|
+
function graphRuntimeModeRecommendation({ platform, hostTotalBytes }) {
|
|
69
|
+
const lowMemoryMac = platform === 'darwin' &&
|
|
70
|
+
Number.isFinite(hostTotalBytes) &&
|
|
71
|
+
hostTotalBytes <= 16 * 1024 ** 3;
|
|
72
|
+
return {
|
|
73
|
+
recommendedMode: lowMemoryMac ? 'native' : 'container',
|
|
74
|
+
reason: lowMemoryMac ? 'low-memory-mac' : 'default',
|
|
75
|
+
hostTotalBytes
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
46
79
|
export async function applyLocalSetup(plan, options, dependencies = {}) {
|
|
47
80
|
const ensureRuntime = dependencies.ensureRuntime ?? ensureGraphRuntime;
|
|
48
81
|
const backupConfig = dependencies.backupConfig ?? backupAgentConfig;
|
|
@@ -58,7 +91,10 @@ export async function applyLocalSetup(plan, options, dependencies = {}) {
|
|
|
58
91
|
port: runtimeSettings.ports.console,
|
|
59
92
|
lan: runtimeSettings.lanAccess,
|
|
60
93
|
runtimeSettings,
|
|
94
|
+
runtimeMode: runtimeSettings.graphRuntimeMode,
|
|
61
95
|
personalOnly: options.personalOnly,
|
|
96
|
+
memoryProfile: options.memoryProfile,
|
|
97
|
+
adaptiveRuntimeSettings: plan.adaptiveRuntimeSettings,
|
|
62
98
|
noStart: options.noStart,
|
|
63
99
|
env: dependencies.env ?? process.env,
|
|
64
100
|
onProgress: dependencies.onProgress
|