neoagent 2.1.15 → 2.1.16
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 +25 -15
- package/lib/manager.js +64 -6
- package/package.json +1 -1
- package/runtime/release_channel.js +126 -0
- package/server/db/database.js +2 -0
- package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
- package/server/public/flutter_bootstrap.js +1 -1
- package/server/public/main.dart.js +40158 -40093
- package/server/routes/agents.js +30 -3
- package/server/routes/settings.js +21 -48
- package/server/services/ai/engine.js +104 -32
- package/server/services/ai/settings.js +1 -1
- package/server/services/ai/systemPrompt.js +1 -1
- package/server/services/ai/tools.js +23 -6
- package/server/services/manager.js +0 -15
- package/server/services/messaging/automation.js +1 -1
- package/server/services/messaging/manager.js +12 -4
- package/server/utils/update_status.js +93 -0
- package/server/utils/version.js +4 -4
package/README.md
CHANGED
|
@@ -1,24 +1,33 @@
|
|
|
1
|
-
<
|
|
1
|
+
<h1 align="center">NeoAgent</h1>
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center"><strong>Your agent. Your server. Your rules.</strong></p>
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/Node.js-18+-5fa04e?style=flat-square&logo=node.js&logoColor=white" alt="Node.js"></a>
|
|
7
|
+
<a href="https://sqlite.org"><img src="https://img.shields.io/badge/SQLite-WAL-003b57?style=flat-square&logo=sqlite&logoColor=white" alt="SQLite"></a>
|
|
8
|
+
<a href="https://flutter.dev"><img src="https://img.shields.io/badge/Flutter-web%20%2B%20android-02569B?style=flat-square&logo=flutter&logoColor=white" alt="Flutter"></a>
|
|
9
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-a855f7?style=flat-square" alt="License"></a>
|
|
10
|
+
</p>
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
<p align="center">
|
|
13
|
+
A self-hosted, proactive AI agent with a Flutter client for web and Android.<br>
|
|
14
|
+
Connects to OpenAI, xAI, Google, MiniMax Code, and local Ollama.<br>
|
|
15
|
+
Runs tasks on a schedule, controls a browser, manages files, and talks to you over Telegram, Discord, or WhatsApp.
|
|
16
|
+
</p>
|
|
11
17
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
| | |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| <img alt="WebUI" src="https://github.com/user-attachments/assets/3c76d59a-b6e3-4698-929b-9c94741ccf1e" height="420"> | <img alt="Mobile Telegram" src="https://github.com/user-attachments/assets/1fd41a9b-5452-4aa4-9478-888c8ad7363a" height="420"> |
|
|
21
|
+
|
|
22
|
+
## Install
|
|
15
23
|
|
|
16
24
|
```bash
|
|
17
25
|
npm install -g neoagent
|
|
18
26
|
neoagent install
|
|
19
27
|
```
|
|
20
28
|
|
|
21
|
-
Manage the
|
|
29
|
+
## Manage the Service
|
|
30
|
+
|
|
22
31
|
```bash
|
|
23
32
|
neoagent status
|
|
24
33
|
neoagent channel beta
|
|
@@ -28,12 +37,13 @@ neoagent logs
|
|
|
28
37
|
```
|
|
29
38
|
|
|
30
39
|
Use `neoagent fix` if a self-edit or broken local install leaves NeoAgent in a bad state. On git installs it backs up runtime data, saves local tracked changes, resets tracked source files, reinstalls dependencies, and restarts the service.
|
|
31
|
-
|
|
40
|
+
|
|
41
|
+
## Links
|
|
32
42
|
|
|
33
43
|
[⚙️ Configuration](docs/configuration.md) · [🧰 Skills](docs/skills.md) · [🐛 Issues](https://github.com/NeoLabs-Systems/NeoAgent/issues)
|
|
34
44
|
|
|
35
45
|
---
|
|
36
46
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
</
|
|
47
|
+
<p align="center">
|
|
48
|
+
Made with ❤️ by <a href="https://github.com/neooriginal">Neo</a> · <a href="https://github.com/NeoLabs-Systems">NeoLabs Systems</a>
|
|
49
|
+
</p>
|
package/lib/manager.js
CHANGED
|
@@ -25,9 +25,11 @@ const {
|
|
|
25
25
|
parseReleaseChannel,
|
|
26
26
|
getReleaseChannelBranch,
|
|
27
27
|
getReleaseChannelDistTag,
|
|
28
|
-
getReleaseChannelLabel,
|
|
29
28
|
readConfiguredReleaseChannel,
|
|
30
29
|
writeReleaseChannelToEnvFile,
|
|
30
|
+
describeReleaseChannelPolicy,
|
|
31
|
+
choosePreferredBranchForChannel,
|
|
32
|
+
choosePreferredNpmTagForChannel,
|
|
31
33
|
} = require('../runtime/release_channel');
|
|
32
34
|
|
|
33
35
|
const APP_NAME = 'NeoAgent';
|
|
@@ -197,8 +199,7 @@ function currentReleaseChannel() {
|
|
|
197
199
|
}
|
|
198
200
|
|
|
199
201
|
function releaseChannelSummary(channel) {
|
|
200
|
-
|
|
201
|
-
return `${getReleaseChannelLabel(normalized)} (branch ${getReleaseChannelBranch(normalized)}, npm ${getReleaseChannelDistTag(normalized)})`;
|
|
202
|
+
return describeReleaseChannelPolicy(parseReleaseChannel(channel) || currentReleaseChannel());
|
|
202
203
|
}
|
|
203
204
|
|
|
204
205
|
function gitWorkingTreeDirty() {
|
|
@@ -214,6 +215,62 @@ function gitRemoteBranchExists(branch) {
|
|
|
214
215
|
return runQuiet('git', ['ls-remote', '--exit-code', '--heads', 'origin', branch]).status === 0;
|
|
215
216
|
}
|
|
216
217
|
|
|
218
|
+
function latestGitTagVersion(pattern) {
|
|
219
|
+
const res = runQuiet('git', ['tag', '--list', pattern, '--sort=-v:refname']);
|
|
220
|
+
if (res.status !== 0) return null;
|
|
221
|
+
const tag = res.stdout
|
|
222
|
+
.split('\n')
|
|
223
|
+
.map((value) => value.trim())
|
|
224
|
+
.find(Boolean);
|
|
225
|
+
return tag ? tag.replace(/^v/, '') : null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function resolvePreferredGitBranch(channel) {
|
|
229
|
+
const normalized = parseReleaseChannel(channel) || currentReleaseChannel();
|
|
230
|
+
if (normalized === 'stable') {
|
|
231
|
+
return getReleaseChannelBranch(normalized);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const stableVersion = latestGitTagVersion('v[0-9]*.[0-9]*.[0-9]*');
|
|
235
|
+
const betaVersion = latestGitTagVersion('v[0-9]*.[0-9]*.[0-9]*-beta.*');
|
|
236
|
+
const preferred = choosePreferredBranchForChannel(normalized, {
|
|
237
|
+
stable: stableVersion,
|
|
238
|
+
beta: betaVersion,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
if (preferred === 'beta' && !gitRemoteBranchExists('beta')) {
|
|
242
|
+
return 'main';
|
|
243
|
+
}
|
|
244
|
+
return preferred;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function resolvePreferredNpmTag(channel) {
|
|
248
|
+
const normalized = parseReleaseChannel(channel) || currentReleaseChannel();
|
|
249
|
+
if (normalized === 'stable') {
|
|
250
|
+
return getReleaseChannelDistTag(normalized);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const distTags = {};
|
|
254
|
+
const tagsRes = runQuiet('npm', ['view', 'neoagent', 'dist-tags', '--json'], {
|
|
255
|
+
env: withInstallEnv(),
|
|
256
|
+
});
|
|
257
|
+
if (tagsRes.status === 0) {
|
|
258
|
+
try {
|
|
259
|
+
const parsed = JSON.parse(tagsRes.stdout || '{}');
|
|
260
|
+
if (parsed && typeof parsed === 'object') {
|
|
261
|
+
Object.assign(distTags, parsed);
|
|
262
|
+
}
|
|
263
|
+
} catch {
|
|
264
|
+
// Ignore parse failures and fall back to the beta tag.
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return choosePreferredNpmTagForChannel(normalized, {
|
|
269
|
+
latest: distTags.latest,
|
|
270
|
+
beta: distTags.beta,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
217
274
|
function ensureGitBranchForReleaseChannel(targetBranch) {
|
|
218
275
|
const branchRes = runQuiet('git', ['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
219
276
|
const currentBranch = branchRes.status === 0 ? branchRes.stdout.trim() : '';
|
|
@@ -692,15 +749,15 @@ function cmdUpdate(args = []) {
|
|
|
692
749
|
process.env.NEOAGENT_RELEASE_CHANNEL = releaseChannel;
|
|
693
750
|
logOk(`Release channel set to ${releaseChannelSummary(releaseChannel)}`);
|
|
694
751
|
}
|
|
695
|
-
const targetBranch = getReleaseChannelBranch(releaseChannel);
|
|
696
|
-
const npmTag = getReleaseChannelDistTag(releaseChannel);
|
|
697
752
|
const versionBefore = currentInstalledVersionLabel();
|
|
698
753
|
let versionAfter = versionBefore;
|
|
699
754
|
|
|
700
755
|
if (fs.existsSync(path.join(APP_DIR, '.git')) && commandExists('git')) {
|
|
701
756
|
const current = runQuiet('git', ['rev-parse', '--short', 'HEAD']);
|
|
702
757
|
|
|
703
|
-
runOrThrow('git', ['fetch', 'origin',
|
|
758
|
+
runOrThrow('git', ['fetch', 'origin', '--tags']);
|
|
759
|
+
const targetBranch = resolvePreferredGitBranch(releaseChannel);
|
|
760
|
+
logInfo(`Using git branch ${targetBranch} for the ${releaseChannel} channel.`);
|
|
704
761
|
ensureGitBranchForReleaseChannel(targetBranch);
|
|
705
762
|
runOrThrow('git', ['pull', '--rebase', '--autostash', 'origin', targetBranch]);
|
|
706
763
|
|
|
@@ -714,6 +771,7 @@ function cmdUpdate(args = []) {
|
|
|
714
771
|
buildBundledWebClientIfPossible();
|
|
715
772
|
}
|
|
716
773
|
} else {
|
|
774
|
+
const npmTag = resolvePreferredNpmTag(releaseChannel);
|
|
717
775
|
logWarn(`No git repo detected; attempting npm global update from ${npmTag}.`);
|
|
718
776
|
if (commandExists('npm')) {
|
|
719
777
|
try {
|
package/package.json
CHANGED
|
@@ -14,6 +14,14 @@ const RELEASE_CHANNEL_DIST_TAGS = Object.freeze({
|
|
|
14
14
|
stable: 'latest',
|
|
15
15
|
beta: 'beta',
|
|
16
16
|
});
|
|
17
|
+
const RELEASE_CHANNEL_BRANCH_POLICIES = Object.freeze({
|
|
18
|
+
stable: 'main only',
|
|
19
|
+
beta: 'newest of beta or main',
|
|
20
|
+
});
|
|
21
|
+
const RELEASE_CHANNEL_NPM_POLICIES = Object.freeze({
|
|
22
|
+
stable: 'latest only',
|
|
23
|
+
beta: 'newest of beta or latest',
|
|
24
|
+
});
|
|
17
25
|
|
|
18
26
|
function parseEnv(raw) {
|
|
19
27
|
const map = new Map();
|
|
@@ -62,6 +70,116 @@ function getReleaseChannelLabel(channel) {
|
|
|
62
70
|
return normalizeReleaseChannel(channel) === 'beta' ? 'Beta' : 'Stable';
|
|
63
71
|
}
|
|
64
72
|
|
|
73
|
+
function parseSemver(version) {
|
|
74
|
+
const match = String(version || '')
|
|
75
|
+
.trim()
|
|
76
|
+
.replace(/^v/, '')
|
|
77
|
+
.match(/^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/);
|
|
78
|
+
if (!match) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
major: Number(match[1]),
|
|
84
|
+
minor: Number(match[2]),
|
|
85
|
+
patch: Number(match[3]),
|
|
86
|
+
prerelease: match[4] ? match[4].split('.') : [],
|
|
87
|
+
raw: match[0],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function comparePrereleasePart(left, right) {
|
|
92
|
+
const leftNumeric = /^\d+$/.test(left);
|
|
93
|
+
const rightNumeric = /^\d+$/.test(right);
|
|
94
|
+
|
|
95
|
+
if (leftNumeric && rightNumeric) {
|
|
96
|
+
return Number(left) - Number(right);
|
|
97
|
+
}
|
|
98
|
+
if (leftNumeric) return -1;
|
|
99
|
+
if (rightNumeric) return 1;
|
|
100
|
+
return left.localeCompare(right);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function compareVersions(leftVersion, rightVersion) {
|
|
104
|
+
const left = parseSemver(leftVersion);
|
|
105
|
+
const right = parseSemver(rightVersion);
|
|
106
|
+
|
|
107
|
+
if (!left && !right) return 0;
|
|
108
|
+
if (!left) return -1;
|
|
109
|
+
if (!right) return 1;
|
|
110
|
+
|
|
111
|
+
for (const key of ['major', 'minor', 'patch']) {
|
|
112
|
+
if (left[key] !== right[key]) {
|
|
113
|
+
return left[key] - right[key];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const leftPre = left.prerelease;
|
|
118
|
+
const rightPre = right.prerelease;
|
|
119
|
+
if (leftPre.length === 0 && rightPre.length === 0) return 0;
|
|
120
|
+
if (leftPre.length === 0) return 1;
|
|
121
|
+
if (rightPre.length === 0) return -1;
|
|
122
|
+
|
|
123
|
+
const length = Math.max(leftPre.length, rightPre.length);
|
|
124
|
+
for (let i = 0; i < length; i++) {
|
|
125
|
+
const leftPart = leftPre[i];
|
|
126
|
+
const rightPart = rightPre[i];
|
|
127
|
+
if (leftPart == null) return -1;
|
|
128
|
+
if (rightPart == null) return 1;
|
|
129
|
+
const diff = comparePrereleasePart(leftPart, rightPart);
|
|
130
|
+
if (diff !== 0) {
|
|
131
|
+
return diff;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function maxVersion(leftVersion, rightVersion) {
|
|
139
|
+
return compareVersions(leftVersion, rightVersion) >= 0 ? leftVersion : rightVersion;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function describeReleaseChannelPolicy(channel) {
|
|
143
|
+
const normalized = normalizeReleaseChannel(channel);
|
|
144
|
+
return `${getReleaseChannelLabel(normalized)} (git ${RELEASE_CHANNEL_BRANCH_POLICIES[normalized]}, npm ${RELEASE_CHANNEL_NPM_POLICIES[normalized]})`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function getReleaseChannelBranchPolicy(channel) {
|
|
148
|
+
return RELEASE_CHANNEL_BRANCH_POLICIES[normalizeReleaseChannel(channel)];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function getReleaseChannelNpmPolicy(channel) {
|
|
152
|
+
return RELEASE_CHANNEL_NPM_POLICIES[normalizeReleaseChannel(channel)];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function choosePreferredBranchForChannel(channel, versions = {}) {
|
|
156
|
+
const normalized = normalizeReleaseChannel(channel);
|
|
157
|
+
if (normalized === 'stable') {
|
|
158
|
+
return 'main';
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const stableVersion = versions.stable;
|
|
162
|
+
const betaVersion = versions.beta;
|
|
163
|
+
if (compareVersions(betaVersion, stableVersion) > 0) {
|
|
164
|
+
return 'beta';
|
|
165
|
+
}
|
|
166
|
+
return 'main';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function choosePreferredNpmTagForChannel(channel, versions = {}) {
|
|
170
|
+
const normalized = normalizeReleaseChannel(channel);
|
|
171
|
+
if (normalized === 'stable') {
|
|
172
|
+
return 'latest';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const stableVersion = versions.latest;
|
|
176
|
+
const betaVersion = versions.beta;
|
|
177
|
+
if (compareVersions(betaVersion, stableVersion) > 0) {
|
|
178
|
+
return 'beta';
|
|
179
|
+
}
|
|
180
|
+
return 'latest';
|
|
181
|
+
}
|
|
182
|
+
|
|
65
183
|
function readReleaseChannelFromRaw(raw) {
|
|
66
184
|
const env = parseEnv(raw);
|
|
67
185
|
return normalizeReleaseChannel(env.get(RELEASE_CHANNEL_ENV_KEY));
|
|
@@ -116,6 +234,14 @@ module.exports = {
|
|
|
116
234
|
getReleaseChannelBranch,
|
|
117
235
|
getReleaseChannelDistTag,
|
|
118
236
|
getReleaseChannelLabel,
|
|
237
|
+
parseSemver,
|
|
238
|
+
compareVersions,
|
|
239
|
+
maxVersion,
|
|
240
|
+
describeReleaseChannelPolicy,
|
|
241
|
+
getReleaseChannelBranchPolicy,
|
|
242
|
+
getReleaseChannelNpmPolicy,
|
|
243
|
+
choosePreferredBranchForChannel,
|
|
244
|
+
choosePreferredNpmTagForChannel,
|
|
119
245
|
readReleaseChannelFromRaw,
|
|
120
246
|
readReleaseChannelFromEnvFile,
|
|
121
247
|
readConfiguredReleaseChannel,
|
package/server/db/database.js
CHANGED
|
@@ -40,6 +40,7 @@ db.exec(`
|
|
|
40
40
|
total_tokens INTEGER DEFAULT 0,
|
|
41
41
|
prompt_metrics TEXT,
|
|
42
42
|
error TEXT,
|
|
43
|
+
final_response TEXT,
|
|
43
44
|
created_at TEXT DEFAULT (datetime('now')),
|
|
44
45
|
updated_at TEXT DEFAULT (datetime('now')),
|
|
45
46
|
completed_at TEXT,
|
|
@@ -389,6 +390,7 @@ for (const col of [
|
|
|
389
390
|
"ALTER TABLE scheduled_tasks ADD COLUMN run_at TEXT",
|
|
390
391
|
"ALTER TABLE scheduled_tasks ADD COLUMN one_time INTEGER DEFAULT 0",
|
|
391
392
|
"ALTER TABLE agent_runs ADD COLUMN prompt_metrics TEXT",
|
|
393
|
+
"ALTER TABLE agent_runs ADD COLUMN final_response TEXT",
|
|
392
394
|
"ALTER TABLE conversations ADD COLUMN summary TEXT",
|
|
393
395
|
"ALTER TABLE conversations ADD COLUMN summary_message_count INTEGER DEFAULT 0",
|
|
394
396
|
"ALTER TABLE conversations ADD COLUMN last_summary TEXT",
|
|
Binary file
|
|
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"052f31d115eceda8cbff1b3481fcde4330c4ae
|
|
|
37
37
|
|
|
38
38
|
_flutter.loader.load({
|
|
39
39
|
serviceWorkerSettings: {
|
|
40
|
-
serviceWorkerVersion: "
|
|
40
|
+
serviceWorkerVersion: "4059413374" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
|
41
41
|
}
|
|
42
42
|
});
|