neoagent 1.4.1 → 1.4.3

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/skills.md CHANGED
@@ -23,6 +23,10 @@ Skills are Markdown files in `~/.neoagent/agent-data/skills/` by default. They a
23
23
  | `tail-log.md` | Tail any log file |
24
24
  | `news-hackernews.md` | Fetch Hacker News top stories |
25
25
  | `qr-code.md` | Generate QR codes |
26
+ | `pdf-toolkit.md` | Inspect, extract, merge, split, and compress PDF files |
27
+ | `git-summary.md` | Summarize git status, branches, commits, and diffs |
28
+ | `csv-toolkit.md` | Inspect and transform CSV/TSV data files |
29
+ | `markdown-workbench.md` | Clean up, outline, and convert Markdown notes/docs |
26
30
 
27
31
  ## Adding a skill
28
32
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "MIT",
6
6
  "main": "server/index.js",
@@ -22,6 +22,8 @@
22
22
  "start": "node server/index.js",
23
23
  "dev": "node --watch server/index.js",
24
24
  "manage": "node bin/neoagent.js",
25
+ "test": "node --test",
26
+ "benchmark:tokens": "node scripts/benchmark-token-cost.js",
25
27
  "release": "npx semantic-release"
26
28
  },
27
29
  "repository": {
@@ -38,6 +38,7 @@ db.exec(`
38
38
  trigger_source TEXT,
39
39
  model TEXT,
40
40
  total_tokens INTEGER DEFAULT 0,
41
+ prompt_metrics TEXT,
41
42
  error TEXT,
42
43
  created_at TEXT DEFAULT (datetime('now')),
43
44
  updated_at TEXT DEFAULT (datetime('now')),
@@ -142,7 +143,10 @@ db.exec(`
142
143
  model TEXT,
143
144
  total_tokens INTEGER DEFAULT 0,
144
145
  compaction_count INTEGER DEFAULT 0,
146
+ summary TEXT,
147
+ summary_message_count INTEGER DEFAULT 0,
145
148
  last_compaction TEXT,
149
+ last_summary TEXT,
146
150
  created_at TEXT DEFAULT (datetime('now')),
147
151
  updated_at TEXT DEFAULT (datetime('now')),
148
152
  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
@@ -226,12 +230,84 @@ db.exec(`
226
230
  CREATE INDEX IF NOT EXISTS idx_core_memory_user ON core_memory(user_id, key);
227
231
  `);
228
232
 
233
+ try {
234
+ db.exec(`
235
+ CREATE VIRTUAL TABLE IF NOT EXISTS conversation_history_fts USING fts5(
236
+ content,
237
+ role UNINDEXED,
238
+ user_id UNINDEXED,
239
+ agent_run_id UNINDEXED,
240
+ tokenize = 'porter unicode61'
241
+ );
242
+
243
+ CREATE TRIGGER IF NOT EXISTS conversation_history_fts_ai AFTER INSERT ON conversation_history BEGIN
244
+ INSERT INTO conversation_history_fts(rowid, content, role, user_id, agent_run_id)
245
+ VALUES (new.id, COALESCE(new.content, ''), new.role, new.user_id, COALESCE(new.agent_run_id, ''));
246
+ END;
247
+
248
+ CREATE TRIGGER IF NOT EXISTS conversation_history_fts_ad AFTER DELETE ON conversation_history BEGIN
249
+ DELETE FROM conversation_history_fts WHERE rowid = old.id;
250
+ END;
251
+
252
+ CREATE TRIGGER IF NOT EXISTS conversation_history_fts_au AFTER UPDATE ON conversation_history BEGIN
253
+ DELETE FROM conversation_history_fts WHERE rowid = old.id;
254
+ INSERT INTO conversation_history_fts(rowid, content, role, user_id, agent_run_id)
255
+ VALUES (new.id, COALESCE(new.content, ''), new.role, new.user_id, COALESCE(new.agent_run_id, ''));
256
+ END;
257
+
258
+ CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(
259
+ content,
260
+ role UNINDEXED,
261
+ user_id UNINDEXED,
262
+ run_id UNINDEXED,
263
+ platform UNINDEXED,
264
+ platform_chat_id UNINDEXED,
265
+ tokenize = 'porter unicode61'
266
+ );
267
+
268
+ CREATE TRIGGER IF NOT EXISTS messages_fts_ai AFTER INSERT ON messages BEGIN
269
+ INSERT INTO messages_fts(rowid, content, role, user_id, run_id, platform, platform_chat_id)
270
+ VALUES (new.id, COALESCE(new.content, ''), new.role, new.user_id, COALESCE(new.run_id, ''), COALESCE(new.platform, ''), COALESCE(new.platform_chat_id, ''));
271
+ END;
272
+
273
+ CREATE TRIGGER IF NOT EXISTS messages_fts_ad AFTER DELETE ON messages BEGIN
274
+ DELETE FROM messages_fts WHERE rowid = old.id;
275
+ END;
276
+
277
+ CREATE TRIGGER IF NOT EXISTS messages_fts_au AFTER UPDATE ON messages BEGIN
278
+ DELETE FROM messages_fts WHERE rowid = old.id;
279
+ INSERT INTO messages_fts(rowid, content, role, user_id, run_id, platform, platform_chat_id)
280
+ VALUES (new.id, COALESCE(new.content, ''), new.role, new.user_id, COALESCE(new.run_id, ''), COALESCE(new.platform, ''), COALESCE(new.platform_chat_id, ''));
281
+ END;
282
+ `);
283
+ } catch {
284
+ // FTS5 is optional. The app still works with LIKE-based fallbacks if unavailable.
285
+ }
286
+
229
287
  // Migrations for existing databases
230
288
  for (const col of [
231
289
  "ALTER TABLE scheduled_tasks ADD COLUMN run_at TEXT",
232
290
  "ALTER TABLE scheduled_tasks ADD COLUMN one_time INTEGER DEFAULT 0",
291
+ "ALTER TABLE agent_runs ADD COLUMN prompt_metrics TEXT",
292
+ "ALTER TABLE conversations ADD COLUMN summary TEXT",
293
+ "ALTER TABLE conversations ADD COLUMN summary_message_count INTEGER DEFAULT 0",
294
+ "ALTER TABLE conversations ADD COLUMN last_summary TEXT",
233
295
  ]) {
234
296
  try { db.exec(col); } catch { /* column already exists */ }
235
297
  }
236
298
 
299
+ try {
300
+ db.exec(`
301
+ INSERT OR REPLACE INTO conversation_history_fts(rowid, content, role, user_id, agent_run_id)
302
+ SELECT id, COALESCE(content, ''), role, user_id, COALESCE(agent_run_id, '')
303
+ FROM conversation_history;
304
+
305
+ INSERT OR REPLACE INTO messages_fts(rowid, content, role, user_id, run_id, platform, platform_chat_id)
306
+ SELECT id, COALESCE(content, ''), role, user_id, COALESCE(run_id, ''), COALESCE(platform, ''), COALESCE(platform_chat_id, '')
307
+ FROM messages;
308
+ `);
309
+ } catch {
310
+ // Older SQLite builds without FTS5 should still let the app boot.
311
+ }
312
+
237
313
  module.exports = db;
@@ -40,13 +40,21 @@
40
40
  </svg>
41
41
  Chat
42
42
  </button>
43
- <button class="sidebar-btn" data-page="activity" id="activityNavBtn">
43
+ <button class="sidebar-btn" data-page="world" id="worldNavBtn">
44
44
  <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
45
45
  stroke-linecap="round" stroke-linejoin="round">
46
- <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
46
+ <path d="M12 2v4" />
47
+ <path d="M12 18v4" />
48
+ <path d="M4.93 4.93l2.83 2.83" />
49
+ <path d="M16.24 16.24l2.83 2.83" />
50
+ <path d="M2 12h4" />
51
+ <path d="M18 12h4" />
52
+ <path d="M4.93 19.07l2.83-2.83" />
53
+ <path d="M16.24 7.76l2.83-2.83" />
54
+ <circle cx="12" cy="12" r="4" />
47
55
  </svg>
48
- Activity
49
- <span class="activity-badge hidden" id="activityBadge"></span>
56
+ World
57
+ <span class="world-badge hidden" id="worldBadge"></span>
50
58
  </button>
51
59
  <div class="sidebar-divider"></div>
52
60
  <button class="sidebar-btn" data-page="messaging">
@@ -174,48 +182,94 @@
174
182
  </div>
175
183
  </div>
176
184
 
177
- <!-- Activity Page -->
178
- <div class="page" id="page-activity">
179
- <div class="activity-container">
180
- <!-- Left Sidebar: Run History -->
181
- <div class="activity-sidebar" id="activitySidebar">
182
- <div class="activity-sidebar-header">
183
- <h2>Run History</h2>
184
- <button class="btn-ghost" id="activityRefreshBtn" title="Refresh">
185
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
186
- stroke-linecap="round" stroke-linejoin="round">
187
- <path d="M21 2v6h-6"></path>
188
- <path d="M3 12a9 9 0 1 0 2.81-6.73L3 8"></path>
189
- <path d="M3 22v-6h6"></path>
190
- <path d="M21 12a9 9 0 1 0-2.81 6.73L21 16"></path>
191
- </svg>
192
- </button>
193
- </div>
194
- <div class="activity-sidebar-list" id="activitySidebarList">
195
- <div class="activity-empty-text">Loading runs...</div>
196
- </div>
197
- </div>
185
+ <!-- World Page -->
186
+ <div class="page" id="page-world">
187
+ <div class="world-page">
188
+ <div class="world-layout">
189
+ <section class="world-stage-card">
190
+ <div class="world-stage-frame">
191
+ <canvas id="worldCanvas" class="world-canvas" aria-label="NeoAgent world visualization"></canvas>
192
+ <div class="world-stage-overlay">
193
+ <div class="world-overlay-group">
194
+ <span class="world-pill" id="worldModePill">Idle</span>
195
+ <span class="world-pill subtle" id="worldToolPill">Awaiting signal</span>
196
+ </div>
197
+ <div class="world-overlay-chip">
198
+ <span class="world-overlay-label">Task</span>
199
+ <span class="world-overlay-value" id="worldTaskValue">No active run</span>
200
+ </div>
201
+ <div class="world-overlay-chip">
202
+ <span class="world-overlay-label">Status</span>
203
+ <span class="world-overlay-value" id="worldStatusValue">Ambient systems nominal</span>
204
+ </div>
205
+ </div>
206
+ </div>
207
+ </section>
198
208
 
199
- <!-- Right Main Area: Timeline -->
200
- <div class="activity-main" id="activityMain">
201
- <div class="activity-main-header">
202
- <h2 id="activityRunTitle">Select a run</h2>
203
- <div class="activity-main-actions">
204
- <span class="atl-run-timer" id="atlTimer" style="display:none;">0s</span>
205
- <span class="atl-run-badge" id="atlRunStatus" style="display:none;"></span>
209
+ <aside class="world-hud">
210
+ <div class="world-panel">
211
+ <div class="world-panel-header">
212
+ <h2>Live Signals</h2>
213
+ <span class="world-panel-kicker">Realtime</span>
214
+ </div>
215
+ <div class="world-stats-grid">
216
+ <div class="world-stat">
217
+ <span class="world-stat-label">Mode</span>
218
+ <strong class="world-stat-value" id="worldModeValue">Idle</strong>
219
+ </div>
220
+ <div class="world-stat">
221
+ <span class="world-stat-label">Run</span>
222
+ <strong class="world-stat-value" id="worldRunValue">None</strong>
223
+ </div>
224
+ <div class="world-stat">
225
+ <span class="world-stat-label">Tools</span>
226
+ <strong class="world-stat-value" id="worldToolsValue">0</strong>
227
+ </div>
228
+ <div class="world-stat">
229
+ <span class="world-stat-label">Helpers</span>
230
+ <strong class="world-stat-value" id="worldHelpersValue">0</strong>
231
+ </div>
232
+ <div class="world-stat">
233
+ <span class="world-stat-label">Messages</span>
234
+ <strong class="world-stat-value" id="worldMessagesValue">0</strong>
235
+ </div>
236
+ </div>
206
237
  </div>
207
- </div>
208
- <div class="atl-viewport" id="activityFeedWrap">
209
- <div class="atl-feed" id="activityFeed">
210
- <div class="atl-empty" id="activityEmpty">
211
- <svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
212
- <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
213
- </svg>
214
- <p>No run selected</p>
215
- <span>Pick a past run or start a new task in chat.</span>
238
+
239
+ <div class="world-panel">
240
+ <div class="world-panel-header">
241
+ <h2>Crew</h2>
242
+ <span class="world-panel-kicker">Lead + Helpers</span>
243
+ </div>
244
+ <div class="world-agent-list" id="worldAgentList">
245
+ <div class="world-empty-state">NeoAgent will show up here as the lead, and helper agents appear when a sub-agent is spawned.</div>
216
246
  </div>
217
247
  </div>
218
- </div>
248
+
249
+ <div class="world-panel">
250
+ <div class="world-panel-header">
251
+ <h2>Event Feed</h2>
252
+ <span class="world-panel-kicker">Latest 6</span>
253
+ </div>
254
+ <div class="world-event-list" id="worldEventList">
255
+ <div class="world-empty-state">The world is idling. Start a task in chat to wake everything up.</div>
256
+ </div>
257
+ </div>
258
+
259
+ <div class="world-panel">
260
+ <div class="world-panel-header">
261
+ <h2>Structures</h2>
262
+ <span class="world-panel-kicker">Role Map</span>
263
+ </div>
264
+ <div class="world-legend">
265
+ <div class="world-legend-item"><span class="swatch core"></span> Core tower</div>
266
+ <div class="world-legend-item"><span class="swatch browser"></span> Browser dock</div>
267
+ <div class="world-legend-item"><span class="swatch memory"></span> Memory archive</div>
268
+ <div class="world-legend-item"><span class="swatch cli"></span> Command forge</div>
269
+ <div class="world-legend-item"><span class="swatch social"></span> Message port</div>
270
+ </div>
271
+ </div>
272
+ </aside>
219
273
  </div>
220
274
  </div>
221
275
  </div>
@@ -268,6 +322,7 @@
268
322
  <div class="page-body">
269
323
  <div class="tabs">
270
324
  <div class="tab active" data-mem-tab="memories">Memories</div>
325
+ <div class="tab" data-mem-tab="sessions">Session Recall</div>
271
326
  <div class="tab" data-mem-tab="core">Core</div>
272
327
  <div class="tab" data-mem-tab="soul">Soul</div>
273
328
  <div class="tab" data-mem-tab="daily">Daily Logs</div>
@@ -292,6 +347,15 @@
292
347
  style="display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px;"></div>
293
348
  </div>
294
349
 
350
+ <div class="mem-panel" id="mem-sessions">
351
+ <div style="display:flex;gap:10px;align-items:center;margin-bottom:14px;flex-wrap:wrap;">
352
+ <input type="text" id="sessionSearchInput" class="input" style="flex:1;min-width:220px;"
353
+ placeholder="Search old chats, commands, or decisions…" />
354
+ <button class="btn btn-sm btn-secondary" id="sessionSearchBtn">Search</button>
355
+ </div>
356
+ <div id="sessionRecallList"></div>
357
+ </div>
358
+
295
359
  <!-- Core memory tab -->
296
360
  <div class="mem-panel" id="mem-core">
297
361
  <div style="margin-bottom:14px;">
@@ -498,36 +562,46 @@
498
562
  </div>
499
563
 
500
564
  <!-- Settings Modal -->
501
- <div class="modal-overlay hidden" id="settingsModal">
565
+ <div class="modal-overlay hidden" id="settingsModal" data-bwignore="true">
502
566
  <div class="modal" style="max-width:550px;">
503
567
  <div class="modal-header">
504
- <h2>Settings</h2>
568
+ <div style="display:flex;align-items:center;gap:8px;">
569
+ <h2>Settings</h2>
570
+ <span class="badge badge-neutral" id="settingsAppVersion" style="font-size:11px;padding:2px 6px;"></span>
571
+ </div>
505
572
  <button class="btn-ghost" id="closeSettings">&times;</button>
506
573
  </div>
507
574
  <div class="modal-body">
508
575
  <div class="form-group">
509
576
  <label class="form-label">Heartbeat</label>
510
577
  <label class="flex items-center gap-2" style="cursor:pointer;">
511
- <input type="checkbox" id="settingHeartbeat">
578
+ <input type="checkbox" id="settingHeartbeat" autocomplete="off" data-bwignore="true">
512
579
  <span>Enable scheduled heartbeat checks</span>
513
580
  </label>
514
581
  </div>
515
582
  <div class="form-group">
516
583
  <label class="form-label">Browser</label>
517
584
  <label class="flex items-center gap-2" style="cursor:pointer;">
518
- <input type="checkbox" id="settingHeadlessBrowser">
585
+ <input type="checkbox" id="settingHeadlessBrowser" autocomplete="off" data-bwignore="true">
519
586
  <span>Run browser headless (no visible window)</span>
520
587
  </label>
521
588
  </div>
589
+ <div class="form-group">
590
+ <label class="form-label">Skill Learning</label>
591
+ <label class="flex items-center gap-2" style="cursor:pointer;">
592
+ <input type="checkbox" id="settingAutoSkillLearning" autocomplete="off" data-bwignore="true">
593
+ <span>Create disabled draft skills from successful multi-step runs</span>
594
+ </label>
595
+ </div>
522
596
  <div class="form-group">
523
597
  <label class="form-label">Default Chat Model</label>
524
- <select id="settingDefaultChatModel" class="input">
598
+ <select id="settingDefaultChatModel" class="input" autocomplete="off" data-bwignore="true">
525
599
  <option value="auto">Smart Selector (Auto)</option>
526
600
  </select>
527
601
  </div>
528
602
  <div class="form-group">
529
603
  <label class="form-label">Default Sub-agent Model</label>
530
- <select id="settingDefaultSubagentModel" class="input">
604
+ <select id="settingDefaultSubagentModel" class="input" autocomplete="off" data-bwignore="true">
531
605
  <option value="auto">Smart Selector (Auto)</option>
532
606
  </select>
533
607
  </div>
@@ -542,7 +616,8 @@
542
616
  <span>Token Usage</span>
543
617
  <span class="settings-info-wrap" tabindex="0" aria-label="Token usage info">
544
618
  <span class="settings-info-icon">i</span>
545
- <span class="settings-info-pop">Run-level totals from the DB. Used to track token usage trends in this app.</span>
619
+ <span class="settings-info-pop">Run-level totals from the DB. Used to track token usage trends in this
620
+ app.</span>
546
621
  </span>
547
622
  </label>
548
623
  <div class="settings-token-box" id="tokenUsageSummary">Loading token usage…</div>