neoagent 2.3.1-beta.85 → 2.3.1-beta.87
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/docs/capabilities.md +2 -0
- package/flutter_app/android/app/src/main/AndroidManifest.xml +14 -0
- package/flutter_app/android/app/src/main/kotlin/com/neoagent/flutter_app/MainActivity.kt +84 -0
- package/flutter_app/lib/main_chat.dart +156 -2
- package/flutter_app/lib/main_controller.dart +137 -10
- package/flutter_app/lib/main_models.dart +69 -0
- package/flutter_app/lib/main_operations.dart +248 -0
- package/flutter_app/lib/main_runtime.dart +11 -2
- package/flutter_app/lib/main_settings.dart +173 -176
- package/flutter_app/lib/main_shared.dart +78 -0
- package/flutter_app/lib/src/app_launch_bridge.dart +39 -10
- package/flutter_app/lib/src/backend_client.dart +28 -0
- package/package.json +1 -1
- package/server/guest-agent.android.package.json +13 -0
- package/server/guest-agent.browser.package.json +14 -0
- package/server/guest_agent.js +61 -44
- package/server/http/routes.js +1 -0
- package/server/public/.last_build_id +1 -1
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +69936 -69277
- package/server/routes/android.js +2 -11
- package/server/routes/browser.js +2 -2
- package/server/routes/memory.js +90 -0
- package/server/routes/social_video.js +62 -0
- package/server/services/ai/capabilityHealth.js +6 -14
- package/server/services/ai/systemPrompt.js +1 -0
- package/server/services/ai/toolResult.js +20 -0
- package/server/services/ai/tools.js +29 -0
- package/server/services/android/android_bootstrap_worker.js +2 -2
- package/server/services/android/controller.js +528 -132
- package/server/services/browser/controller.js +51 -68
- package/server/services/manager.js +15 -0
- package/server/services/memory/llm_transfer.js +217 -0
- package/server/services/runtime/backends/local-vm.js +16 -3
- package/server/services/runtime/guest_bootstrap.js +224 -56
- package/server/services/runtime/manager.js +53 -15
- package/server/services/runtime/qemu.js +149 -24
- package/server/services/runtime/settings.js +9 -14
- package/server/services/runtime/validation.js +10 -11
- package/server/services/social_video/adapters/base.js +26 -0
- package/server/services/social_video/adapters/index.js +27 -0
- package/server/services/social_video/adapters/instagram.js +17 -0
- package/server/services/social_video/adapters/tiktok.js +17 -0
- package/server/services/social_video/adapters/x.js +17 -0
- package/server/services/social_video/adapters/youtube.js +17 -0
- package/server/services/social_video/captions.js +187 -0
- package/server/services/social_video/frame.js +42 -0
- package/server/services/social_video/index.js +7 -0
- package/server/services/social_video/metadata.js +63 -0
- package/server/services/social_video/result.js +63 -0
- package/server/services/social_video/service.js +576 -0
- package/server/services/social_video/url.js +83 -0
- package/server/utils/deployment.js +4 -4
- package/server/guest-agent.package.json +0 -15
|
@@ -26,6 +26,7 @@ const VIEWPORTS = [
|
|
|
26
26
|
|
|
27
27
|
function resolveBrowserExecutablePath() {
|
|
28
28
|
const explicitPath =
|
|
29
|
+
process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH ||
|
|
29
30
|
process.env.PUPPETEER_EXECUTABLE_PATH ||
|
|
30
31
|
process.env.CHROME_BIN ||
|
|
31
32
|
process.env.CHROMIUM_BIN;
|
|
@@ -34,7 +35,6 @@ function resolveBrowserExecutablePath() {
|
|
|
34
35
|
|
|
35
36
|
const bundledCandidates = [
|
|
36
37
|
() => require('playwright-chromium').chromium.executablePath(),
|
|
37
|
-
() => require('playwright').chromium.executablePath(),
|
|
38
38
|
];
|
|
39
39
|
for (const resolveBundled of bundledCandidates) {
|
|
40
40
|
try {
|
|
@@ -76,22 +76,11 @@ function resolveBrowserExecutablePath() {
|
|
|
76
76
|
return platformCandidates.find((candidate) => fs.existsSync(candidate)) || null;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
function resolveFirefoxExecutablePath() {
|
|
80
|
-
try {
|
|
81
|
-
const bundledPath = require('playwright').firefox.executablePath();
|
|
82
|
-
return bundledPath && fs.existsSync(bundledPath) ? bundledPath : null;
|
|
83
|
-
} catch {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
79
|
function installPlaywrightBrowserBinary(browserName) {
|
|
89
|
-
const packageRoot = path.dirname(require.resolve('playwright/package.json'));
|
|
80
|
+
const packageRoot = path.dirname(require.resolve('playwright-chromium/package.json'));
|
|
90
81
|
const cliPath = path.join(packageRoot, 'cli.js');
|
|
91
82
|
return new Promise((resolve, reject) => {
|
|
92
|
-
const args =
|
|
93
|
-
? [cliPath, 'install', '--no-shell', 'chromium']
|
|
94
|
-
: [cliPath, 'install', browserName];
|
|
83
|
+
const args = [cliPath, 'install', '--no-shell', browserName];
|
|
95
84
|
const child = spawn(process.execPath, args, {
|
|
96
85
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
97
86
|
});
|
|
@@ -161,13 +150,29 @@ function normalizeWaitUntil(waitUntil) {
|
|
|
161
150
|
return 'domcontentloaded';
|
|
162
151
|
}
|
|
163
152
|
|
|
153
|
+
function clearChromiumSingletonLocks(profileDir) {
|
|
154
|
+
const lockEntries = [
|
|
155
|
+
'SingletonLock',
|
|
156
|
+
'SingletonSocket',
|
|
157
|
+
'SingletonCookie',
|
|
158
|
+
'SingletonStartupLock',
|
|
159
|
+
'DevToolsActivePort',
|
|
160
|
+
];
|
|
161
|
+
for (const entry of lockEntries) {
|
|
162
|
+
const targetPath = path.join(profileDir, entry);
|
|
163
|
+
try {
|
|
164
|
+
fs.rmSync(targetPath, { force: true, recursive: true });
|
|
165
|
+
} catch {}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
164
169
|
class BrowserController {
|
|
165
170
|
constructor(options = {}) {
|
|
166
171
|
this.io = options.io || null;
|
|
167
172
|
this.userId = options.userId != null ? String(options.userId) : null;
|
|
168
173
|
this.artifactStore = options.artifactStore || null;
|
|
169
174
|
this.runtimeBackend = options.runtimeBackend || 'host';
|
|
170
|
-
this.engine =
|
|
175
|
+
this.engine = 'chromium';
|
|
171
176
|
this.browser = null;
|
|
172
177
|
this.context = null;
|
|
173
178
|
this.page = null;
|
|
@@ -327,9 +332,7 @@ class BrowserController {
|
|
|
327
332
|
this._userAgent = USER_AGENTS[rand(0, USER_AGENTS.length - 1)];
|
|
328
333
|
this._viewport = VIEWPORTS[rand(0, VIEWPORTS.length - 1)];
|
|
329
334
|
|
|
330
|
-
let executablePath =
|
|
331
|
-
? resolveFirefoxExecutablePath()
|
|
332
|
-
: resolveBrowserExecutablePath();
|
|
335
|
+
let executablePath = resolveBrowserExecutablePath();
|
|
333
336
|
if (!executablePath) {
|
|
334
337
|
if (!this.browserBinaryInstallPromise) {
|
|
335
338
|
this.browserBinaryInstallPromise = installPlaywrightBrowserBinary(this.engine);
|
|
@@ -339,9 +342,7 @@ class BrowserController {
|
|
|
339
342
|
} finally {
|
|
340
343
|
this.browserBinaryInstallPromise = null;
|
|
341
344
|
}
|
|
342
|
-
executablePath =
|
|
343
|
-
? resolveFirefoxExecutablePath()
|
|
344
|
-
: resolveBrowserExecutablePath();
|
|
345
|
+
executablePath = resolveBrowserExecutablePath();
|
|
345
346
|
}
|
|
346
347
|
|
|
347
348
|
if (!executablePath) {
|
|
@@ -353,53 +354,35 @@ class BrowserController {
|
|
|
353
354
|
...(this.displayValue ? { DISPLAY: this.displayValue } : {}),
|
|
354
355
|
};
|
|
355
356
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
'--disable-dev-shm-usage',
|
|
386
|
-
'--disable-crash-reporter',
|
|
387
|
-
'--disable-background-networking',
|
|
388
|
-
'--disable-component-update',
|
|
389
|
-
'--disable-blink-features=AutomationControlled',
|
|
390
|
-
'--disable-infobars',
|
|
391
|
-
'--no-first-run',
|
|
392
|
-
'--no-default-browser-check',
|
|
393
|
-
'--disable-gpu',
|
|
394
|
-
'--lang=en-US,en',
|
|
395
|
-
`--window-size=${this._viewport.width},${this._viewport.height}`,
|
|
396
|
-
],
|
|
397
|
-
defaultViewport: this._viewport,
|
|
398
|
-
ignoreDefaultArgs: ['--enable-automation'],
|
|
399
|
-
timeout: 120000,
|
|
400
|
-
});
|
|
401
|
-
this.page = await this.browser.newPage();
|
|
402
|
-
}
|
|
357
|
+
const launchArgs = [
|
|
358
|
+
'--no-sandbox',
|
|
359
|
+
'--disable-setuid-sandbox',
|
|
360
|
+
'--disable-dev-shm-usage',
|
|
361
|
+
'--disable-crash-reporter',
|
|
362
|
+
'--disable-background-networking',
|
|
363
|
+
'--disable-component-update',
|
|
364
|
+
'--disable-blink-features=AutomationControlled',
|
|
365
|
+
'--disable-infobars',
|
|
366
|
+
'--no-first-run',
|
|
367
|
+
'--no-default-browser-check',
|
|
368
|
+
'--disable-gpu',
|
|
369
|
+
'--lang=en-US,en',
|
|
370
|
+
`--window-size=${this._viewport.width},${this._viewport.height}`,
|
|
371
|
+
];
|
|
372
|
+
|
|
373
|
+
const playwright = require('playwright-chromium');
|
|
374
|
+
clearChromiumSingletonLocks(this.profileDir);
|
|
375
|
+
this.context = await playwright.chromium.launchPersistentContext(this.profileDir, {
|
|
376
|
+
headless: false,
|
|
377
|
+
executablePath,
|
|
378
|
+
env: launchEnv,
|
|
379
|
+
args: launchArgs,
|
|
380
|
+
viewport: this._viewport,
|
|
381
|
+
ignoreHTTPSErrors: false,
|
|
382
|
+
timeout: 120000,
|
|
383
|
+
});
|
|
384
|
+
this.browser = typeof this.context.browser === 'function' ? this.context.browser() : null;
|
|
385
|
+
this.page = this.context.pages()[0] || await this.context.newPage();
|
|
403
386
|
await this._applyStealthToPage(this.page);
|
|
404
387
|
})();
|
|
405
388
|
|
|
@@ -13,6 +13,7 @@ const { WidgetService } = require('./widgets/service');
|
|
|
13
13
|
const { setupWebSocket } = require('./websocket');
|
|
14
14
|
const { registerMessagingAutomation } = require('./messaging/automation');
|
|
15
15
|
const { RecordingManager } = require('./recordings/manager');
|
|
16
|
+
const { SocialVideoService } = require('./social_video');
|
|
16
17
|
const { VoiceRuntimeManager } = require('./voice/runtimeManager');
|
|
17
18
|
const { AuthProviderManager } = require('./account/auth_provider_manager');
|
|
18
19
|
const { IntegrationManager } = require('./integrations/manager');
|
|
@@ -318,6 +319,19 @@ function createRecordingManager(app, io) {
|
|
|
318
319
|
return recordingManager;
|
|
319
320
|
}
|
|
320
321
|
|
|
322
|
+
function createSocialVideoService(app) {
|
|
323
|
+
const socialVideoService = registerLocal(
|
|
324
|
+
app,
|
|
325
|
+
'socialVideoService',
|
|
326
|
+
new SocialVideoService({
|
|
327
|
+
artifactStore: app.locals.artifactStore,
|
|
328
|
+
runtimeManager: app.locals.runtimeManager,
|
|
329
|
+
}),
|
|
330
|
+
);
|
|
331
|
+
logServiceReady('Social video service ready');
|
|
332
|
+
return socialVideoService;
|
|
333
|
+
}
|
|
334
|
+
|
|
321
335
|
function createWidgetService(app) {
|
|
322
336
|
const widgetService = registerLocal(
|
|
323
337
|
app,
|
|
@@ -460,6 +474,7 @@ async function startServices(app, io) {
|
|
|
460
474
|
|
|
461
475
|
const messagingManager = createMessagingManager(app, io, agentEngine);
|
|
462
476
|
const recordingManager = createRecordingManager(app, io);
|
|
477
|
+
createSocialVideoService(app);
|
|
463
478
|
createWidgetService(app);
|
|
464
479
|
createWearableService(app);
|
|
465
480
|
createScreenRecorder(app);
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
const HEADING_ALIASES = {
|
|
2
|
+
profile: 'identity',
|
|
3
|
+
identity: 'identity',
|
|
4
|
+
preferences: 'preferences',
|
|
5
|
+
preference: 'preferences',
|
|
6
|
+
projects: 'projects',
|
|
7
|
+
project: 'projects',
|
|
8
|
+
contacts: 'contacts',
|
|
9
|
+
contact: 'contacts',
|
|
10
|
+
events: 'events',
|
|
11
|
+
event: 'events',
|
|
12
|
+
tasks: 'tasks',
|
|
13
|
+
task: 'tasks',
|
|
14
|
+
'assistant self': 'assistant_self',
|
|
15
|
+
self: 'assistant_self',
|
|
16
|
+
'behavior notes': '__behavior_notes',
|
|
17
|
+
'assistant behavior notes': '__behavior_notes',
|
|
18
|
+
'core memory': '__core',
|
|
19
|
+
'core memories': '__core',
|
|
20
|
+
'other memories': 'episodic',
|
|
21
|
+
other: 'episodic',
|
|
22
|
+
misc: 'episodic',
|
|
23
|
+
miscellaneous: 'episodic',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const SECTION_PRIORITY = {
|
|
27
|
+
identity: 8,
|
|
28
|
+
preferences: 7,
|
|
29
|
+
projects: 6,
|
|
30
|
+
contacts: 6,
|
|
31
|
+
events: 6,
|
|
32
|
+
tasks: 6,
|
|
33
|
+
assistant_self: 7,
|
|
34
|
+
episodic: 5,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const MAX_MEMORY_LENGTH = 1200;
|
|
38
|
+
const MAX_MEMORIES = 200;
|
|
39
|
+
|
|
40
|
+
function normalizeHeadingLabel(input) {
|
|
41
|
+
return String(input || '')
|
|
42
|
+
.trim()
|
|
43
|
+
.replace(/^#+\s*/, '')
|
|
44
|
+
.replace(/[:]+$/g, '')
|
|
45
|
+
.replace(/\s+/g, ' ')
|
|
46
|
+
.toLowerCase();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function detectHeading(line) {
|
|
50
|
+
const trimmed = String(line || '').trim();
|
|
51
|
+
if (!trimmed) return null;
|
|
52
|
+
const normalized = normalizeHeadingLabel(trimmed);
|
|
53
|
+
if (HEADING_ALIASES[normalized]) return normalized;
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function splitSections(text) {
|
|
58
|
+
const lines = String(text || '').split(/\r?\n/);
|
|
59
|
+
const sections = [];
|
|
60
|
+
let current = { heading: 'other', lines: [] };
|
|
61
|
+
|
|
62
|
+
for (const line of lines) {
|
|
63
|
+
const heading = detectHeading(line);
|
|
64
|
+
if (heading) {
|
|
65
|
+
if (current.lines.length || current.heading) {
|
|
66
|
+
sections.push(current);
|
|
67
|
+
}
|
|
68
|
+
current = { heading, lines: [] };
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
current.lines.push(line);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (current.lines.length || current.heading) {
|
|
75
|
+
sections.push(current);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return sections;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function collectBulletItems(lines) {
|
|
82
|
+
const items = [];
|
|
83
|
+
for (const line of lines) {
|
|
84
|
+
const match = String(line || '').match(/^\s*(?:[-*]|\u2022|\d+\.)\s+(.*)$/);
|
|
85
|
+
if (match && match[1]) {
|
|
86
|
+
const value = match[1].trim();
|
|
87
|
+
if (value) items.push(value);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return items;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function collapseParagraph(lines) {
|
|
94
|
+
const parts = lines
|
|
95
|
+
.map((line) => String(line || '').trim())
|
|
96
|
+
.filter((line) => line.length > 0);
|
|
97
|
+
if (!parts.length) return '';
|
|
98
|
+
return parts.join(' ');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function normalizeMemoryContent(text) {
|
|
102
|
+
const cleaned = String(text || '').trim();
|
|
103
|
+
if (!cleaned) return '';
|
|
104
|
+
if (cleaned.length <= MAX_MEMORY_LENGTH) return cleaned;
|
|
105
|
+
return cleaned.slice(0, MAX_MEMORY_LENGTH).trim();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function buildLlmTransferPrompt({ agentLabel = 'NeoAgent' } = {}) {
|
|
109
|
+
return [
|
|
110
|
+
'You are preparing a memory export for ' + agentLabel + '.',
|
|
111
|
+
'Return a concise, structured, natural language summary of everything you remember about the user.',
|
|
112
|
+
'',
|
|
113
|
+
'Rules:',
|
|
114
|
+
'- Use only plain text. No JSON or code blocks.',
|
|
115
|
+
'- Use short bullet points where possible.',
|
|
116
|
+
'- Omit secrets, passwords, API keys, or anything sensitive.',
|
|
117
|
+
'- If a section has no data, omit the section.',
|
|
118
|
+
'',
|
|
119
|
+
'Use these sections and formatting:',
|
|
120
|
+
'# Profile',
|
|
121
|
+
'- Key identity facts about the user.',
|
|
122
|
+
'# Preferences',
|
|
123
|
+
'- Stable preferences, likes, dislikes, habits.',
|
|
124
|
+
'# Projects',
|
|
125
|
+
'- Ongoing projects, goals, responsibilities.',
|
|
126
|
+
'# Contacts',
|
|
127
|
+
'- Important people or organizations and the relationship.',
|
|
128
|
+
'# Events',
|
|
129
|
+
'- Important dates or recurring events.',
|
|
130
|
+
'# Tasks',
|
|
131
|
+
'- Open tasks or commitments the user expects to remember.',
|
|
132
|
+
'# Behavior Notes',
|
|
133
|
+
'Short guidance for how the assistant should behave.',
|
|
134
|
+
'# Core Memory',
|
|
135
|
+
'key: value entries for critical facts that should always be pinned.',
|
|
136
|
+
'# Other Memories',
|
|
137
|
+
'- Anything else that does not fit above.',
|
|
138
|
+
].join('\n');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function parseLlmTransferText(text) {
|
|
142
|
+
const sections = splitSections(text);
|
|
143
|
+
const memories = [];
|
|
144
|
+
const coreEntries = {};
|
|
145
|
+
let behaviorNotes = '';
|
|
146
|
+
const warnings = [];
|
|
147
|
+
|
|
148
|
+
for (const section of sections) {
|
|
149
|
+
const heading = normalizeHeadingLabel(section.heading || 'other');
|
|
150
|
+
const alias = HEADING_ALIASES[heading] || 'episodic';
|
|
151
|
+
const lines = section.lines || [];
|
|
152
|
+
|
|
153
|
+
if (alias === '__behavior_notes') {
|
|
154
|
+
const notes = collapseParagraph(lines);
|
|
155
|
+
if (notes) behaviorNotes = notes;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (alias === '__core') {
|
|
160
|
+
for (const rawLine of lines) {
|
|
161
|
+
const line = String(rawLine || '').trim();
|
|
162
|
+
if (!line) continue;
|
|
163
|
+
const cleanedLine = line.replace(/^[-*]\s*/, '');
|
|
164
|
+
const colonIndex = cleanedLine.indexOf(':');
|
|
165
|
+
if (colonIndex <= 0) continue;
|
|
166
|
+
const key = cleanedLine.slice(0, colonIndex).trim();
|
|
167
|
+
const value = cleanedLine.slice(colonIndex + 1).trim();
|
|
168
|
+
if (!key || !value) continue;
|
|
169
|
+
if (key === 'active_context') continue;
|
|
170
|
+
coreEntries[key] = value;
|
|
171
|
+
}
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const bulletItems = collectBulletItems(lines);
|
|
176
|
+
if (bulletItems.length) {
|
|
177
|
+
for (const item of bulletItems) {
|
|
178
|
+
const content = normalizeMemoryContent(item);
|
|
179
|
+
if (!content) continue;
|
|
180
|
+
memories.push({
|
|
181
|
+
category: alias,
|
|
182
|
+
content,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const paragraph = normalizeMemoryContent(collapseParagraph(lines));
|
|
189
|
+
if (paragraph) {
|
|
190
|
+
memories.push({
|
|
191
|
+
category: alias,
|
|
192
|
+
content: paragraph,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (memories.length > MAX_MEMORIES) {
|
|
198
|
+
warnings.push('Import exceeded ' + MAX_MEMORIES + ' items; extra entries were skipped.');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
memories: memories.slice(0, MAX_MEMORIES),
|
|
203
|
+
coreEntries,
|
|
204
|
+
behaviorNotes,
|
|
205
|
+
warnings,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function importanceForCategory(category) {
|
|
210
|
+
return SECTION_PRIORITY[category] || 5;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
module.exports = {
|
|
214
|
+
buildLlmTransferPrompt,
|
|
215
|
+
parseLlmTransferText,
|
|
216
|
+
importanceForCategory,
|
|
217
|
+
};
|
|
@@ -23,6 +23,18 @@ function assertPathInside(baseDir, candidatePath, label) {
|
|
|
23
23
|
return resolvedCandidate;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
function isPidAlive(pid) {
|
|
27
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
process.kill(pid, 0);
|
|
32
|
+
return true;
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
26
38
|
class RuntimeHttpClient {
|
|
27
39
|
constructor(baseUrl, token = '', options = {}) {
|
|
28
40
|
this.baseUrl = String(baseUrl || '').replace(/\/+$/, '');
|
|
@@ -341,6 +353,7 @@ class VmAndroidProvider {
|
|
|
341
353
|
class LocalVmExecutionBackend {
|
|
342
354
|
constructor(options = {}) {
|
|
343
355
|
this.vmManager = options.vmManager;
|
|
356
|
+
this.runtimeProfile = options.runtimeProfile === 'android' ? 'android' : 'browser_cli';
|
|
344
357
|
this.token = options.token || process.env.NEOAGENT_VM_GUEST_TOKEN || '';
|
|
345
358
|
this.artifactStore = options.artifactStore || null;
|
|
346
359
|
this.lastActivity = new Map();
|
|
@@ -364,12 +377,12 @@ class LocalVmExecutionBackend {
|
|
|
364
377
|
const now = Date.now();
|
|
365
378
|
for (const [userId, lastUsed] of this.lastActivity.entries()) {
|
|
366
379
|
if (now - lastUsed > IDLE_TIMEOUT_MS) {
|
|
367
|
-
console.log(`[Runtime] User ${userId} runtime idle for ${Math.round((now - lastUsed) / 1000)}s, shutting down VM.`);
|
|
380
|
+
console.log(`[Runtime:${this.runtimeProfile}] User ${userId} runtime idle for ${Math.round((now - lastUsed) / 1000)}s, shutting down VM.`);
|
|
368
381
|
this.lastActivity.delete(userId);
|
|
369
382
|
try {
|
|
370
383
|
await this.vmManager?.killVm?.(userId);
|
|
371
384
|
} catch (err) {
|
|
372
|
-
console.error(`[Runtime] Failed to shut down idle VM for user ${userId}:`, err.message);
|
|
385
|
+
console.error(`[Runtime:${this.runtimeProfile}] Failed to shut down idle VM for user ${userId}:`, err.message);
|
|
373
386
|
}
|
|
374
387
|
}
|
|
375
388
|
}
|
|
@@ -391,7 +404,7 @@ class LocalVmExecutionBackend {
|
|
|
391
404
|
checkLiveness: () => {
|
|
392
405
|
const key = String(userId || '').trim();
|
|
393
406
|
const session = this.vmManager.instances.get(key);
|
|
394
|
-
return session && session.process &&
|
|
407
|
+
return Boolean(session && session.process && isPidAlive(session.process.pid));
|
|
395
408
|
},
|
|
396
409
|
});
|
|
397
410
|
} catch (error) {
|