omnius 1.0.595 → 1.0.597

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.
@@ -1417,9 +1417,8 @@ var EpisodeStore = class {
1417
1417
  });
1418
1418
  scored.sort((a, b) => b.score - a.score);
1419
1419
  const topK = scored.slice(0, limit);
1420
- const updateStmt = this.db.prepare("UPDATE episodes SET strength = strength + 1, last_retrieved = ? WHERE id = ?");
1421
- for (const { episode } of topK) {
1422
- updateStmt.run(now, episode.id);
1420
+ if (opts.recordRetrieval !== false) {
1421
+ this.recordRetrieved(topK.map((item) => item.episode), now);
1423
1422
  }
1424
1423
  return topK.map((s) => s.episode);
1425
1424
  }
@@ -1427,30 +1426,105 @@ var EpisodeStore = class {
1427
1426
  * Falls back to standard search if graph is unavailable or no entities found.
1428
1427
  */
1429
1428
  searchWithPPR(query, opts = {}) {
1430
- const standardResults = this.search(query, opts);
1431
1429
  if (!this.graph || !query.query) {
1432
- return standardResults;
1430
+ return this.search(query, opts);
1433
1431
  }
1432
+ const standardResults = this.search(query, { ...opts, recordRetrieval: false });
1434
1433
  try {
1435
1434
  const pprResult = retrieveByPPR(query.query, this.graph, this, {
1436
1435
  damping: 0.5,
1437
1436
  maxIterations: 50,
1438
1437
  convergenceThreshold: 1e-6,
1439
- topK: query.limit ?? 20
1438
+ topK: query.limit ?? 20,
1439
+ currentEmotionalState: opts.currentEmotionalState,
1440
+ influenceFor: opts.influenceFor ? (episodeId) => {
1441
+ const episode = this.get(episodeId);
1442
+ return episode ? opts.influenceFor(episode) : null;
1443
+ } : void 0,
1444
+ engagementWeight: opts.engagementWeight
1440
1445
  });
1441
- const merged = [...standardResults];
1442
- const seenIds = new Set(merged.map((e) => e.id));
1443
- for (const { episode } of pprResult.episodes) {
1444
- if (!seenIds.has(episode.id)) {
1445
- merged.push(episode);
1446
- seenIds.add(episode.id);
1446
+ const candidates = /* @__PURE__ */ new Map();
1447
+ const addCandidate = (episode, rank, pprScore = 0) => {
1448
+ if (!this.matchesQueryFilters(episode, query))
1449
+ return;
1450
+ const contribution = 1 / (60 + rank + 1);
1451
+ const existing = candidates.get(episode.id);
1452
+ if (existing) {
1453
+ existing.score += contribution;
1454
+ existing.sourceCount += 1;
1455
+ existing.bestPprScore = Math.max(existing.bestPprScore, pprScore);
1456
+ } else {
1457
+ candidates.set(episode.id, {
1458
+ episode,
1459
+ score: contribution,
1460
+ sourceCount: 1,
1461
+ bestPprScore: pprScore
1462
+ });
1447
1463
  }
1448
- }
1449
- return merged.slice(0, query.limit ?? 20);
1464
+ };
1465
+ standardResults.forEach((episode, rank) => addCandidate(episode, rank));
1466
+ pprResult.episodes.forEach(({ episode, pprScore }, rank) => {
1467
+ addCandidate(episode, rank, pprScore);
1468
+ });
1469
+ const limit = query.limit ?? 20;
1470
+ const merged = [...candidates.values()].sort((a, b) => b.score - a.score || b.sourceCount - a.sourceCount || b.bestPprScore - a.bestPprScore || b.episode.timestamp - a.episode.timestamp || a.episode.id.localeCompare(b.episode.id)).slice(0, limit).map((candidate) => candidate.episode);
1471
+ if (opts.recordRetrieval !== false)
1472
+ this.recordRetrieved(merged, Date.now());
1473
+ return merged;
1450
1474
  } catch {
1475
+ if (opts.recordRetrieval !== false)
1476
+ this.recordRetrieved(standardResults, Date.now());
1451
1477
  return standardResults;
1452
1478
  }
1453
1479
  }
1480
+ /** Apply every non-text relevance constraint to a graph candidate. */
1481
+ matchesQueryFilters(episode, query) {
1482
+ if (query.sessionId && episode.sessionId !== query.sessionId)
1483
+ return false;
1484
+ if (query.taskId && episode.taskId !== query.taskId)
1485
+ return false;
1486
+ if (query.toolName && episode.toolName !== query.toolName)
1487
+ return false;
1488
+ if (query.modality && episode.modality !== query.modality)
1489
+ return false;
1490
+ if (query.since !== void 0 && episode.timestamp < query.since)
1491
+ return false;
1492
+ if (query.until !== void 0 && episode.timestamp > query.until)
1493
+ return false;
1494
+ if (query.minImportance !== void 0 && episode.importance < query.minImportance)
1495
+ return false;
1496
+ const metadata = episode.metadata;
1497
+ if (query.metadataFilter) {
1498
+ if (!metadata)
1499
+ return false;
1500
+ for (const [key, value] of Object.entries(query.metadataFilter)) {
1501
+ if (metadata[key] !== value)
1502
+ return false;
1503
+ }
1504
+ }
1505
+ if (query.soundClass && metadata?.["sound_class"] !== query.soundClass)
1506
+ return false;
1507
+ if (query.rmsRange) {
1508
+ const rms = metadata?.["rms_db"] ?? metadata?.["rmsDb"];
1509
+ if (typeof rms !== "number")
1510
+ return false;
1511
+ if (query.rmsRange.min !== void 0 && rms < query.rmsRange.min)
1512
+ return false;
1513
+ if (query.rmsRange.max !== void 0 && rms > query.rmsRange.max)
1514
+ return false;
1515
+ }
1516
+ return true;
1517
+ }
1518
+ recordRetrieved(episodes, timestamp) {
1519
+ if (episodes.length === 0)
1520
+ return;
1521
+ const updateStmt = this.db.prepare("UPDATE episodes SET strength = strength + 1, last_retrieved = ? WHERE id = ?");
1522
+ const updateAll = this.db.transaction(() => {
1523
+ for (const episode of episodes)
1524
+ updateStmt.run(timestamp, episode.id);
1525
+ });
1526
+ updateAll();
1527
+ }
1454
1528
  /** Get a single episode by ID. */
1455
1529
  get(id) {
1456
1530
  const row = this.db.prepare("SELECT * FROM episodes WHERE id = ?").get(id);
@@ -930,6 +930,11 @@ function main() {
930
930
  progress(4, INSTALL_STEPS, "Ensuring local REST key");
931
931
  ensureBootstrapApiKeyForUser(effectiveUser());
932
932
 
933
+ if (process.env.OMNIUS_UPDATE_COORDINATED === "1") {
934
+ log("Coordinated update owns daemon restart and exact-version verification.");
935
+ return safeExit(0);
936
+ }
937
+
933
938
  if (process.env.OMNIUS_SKIP_DAEMON_INSTALL === "1") {
934
939
  log("OMNIUS_SKIP_DAEMON_INSTALL=1 — skipping daemon service install.");
935
940
  return safeExit(0);
@@ -13,6 +13,14 @@
13
13
 
14
14
  "use strict";
15
15
 
16
+ // A coordinated self-update owns the exact PID/service lifecycle and verifies
17
+ // the replacement runtime afterward. The generic install hook must not race
18
+ // it by sweeping the configured port or killing an unattested process.
19
+ if (process.env.OMNIUS_UPDATE_COORDINATED === "1") {
20
+ process.stdout.write(" [preinstall] coordinated update owns daemon handoff\n");
21
+ process.exit(0);
22
+ }
23
+
16
24
  if (process.env.OMNIUS_SKIP_DAEMON_INSTALL === "1") {
17
25
  process.exit(0);
18
26
  }
@@ -60,6 +60,7 @@ export default defineConfig({
60
60
  { text: "Realtime Conversation", link: "/guides/realtime" },
61
61
  { text: "Telegram", link: "/guides/telegram" },
62
62
  { text: "Media Generation", link: "/guides/media-generation" },
63
+ { text: "System Tray Indicator", link: "/guides/system-tray" },
63
64
  ],
64
65
  },
65
66
  {
@@ -11003,6 +11003,32 @@
11003
11003
  }
11004
11004
  ]
11005
11005
  },
11006
+ {
11007
+ "id": "guide.guides-system-tray",
11008
+ "kind": "guide",
11009
+ "title": "System Tray Indicator",
11010
+ "summary": "Bundled Omnius documentation artifact: docs/guides/system-tray.md.",
11011
+ "keywords": [
11012
+ "guides",
11013
+ "system",
11014
+ "tray",
11015
+ "md"
11016
+ ],
11017
+ "maturity": "stable",
11018
+ "interfaces": [
11019
+ {
11020
+ "type": "file",
11021
+ "target": "docs/guides/system-tray.md"
11022
+ }
11023
+ ],
11024
+ "references": [
11025
+ {
11026
+ "type": "documentation",
11027
+ "target": "docs/guides/system-tray.md",
11028
+ "relation": "canonical-artifact"
11029
+ }
11030
+ ]
11031
+ },
11006
11032
  {
11007
11033
  "id": "guide.guides-telegram",
11008
11034
  "kind": "guide",
package/docs/DISCOVERY.md CHANGED
@@ -361,6 +361,7 @@ Daemon equivalents are `GET /v1/discovery?q=<intent>` and `GET /v1/discovery/{id
361
361
  | `guide.guides-osint-research` | Categorized OSINT research | Bundled Omnius documentation artifact: docs/guides/osint-research.md. |
362
362
  | `guide.guides-realtime` | Realtime Conversations | Bundled Omnius documentation artifact: docs/guides/realtime.md. |
363
363
  | `guide.guides-sponsor-and-cohere` | Sponsor And COHERE Mesh | Bundled Omnius documentation artifact: docs/guides/sponsor-and-cohere.md. |
364
+ | `guide.guides-system-tray` | System Tray Indicator | Bundled Omnius documentation artifact: docs/guides/system-tray.md. |
364
365
  | `guide.guides-telegram` | Telegram Bridge | Bundled Omnius documentation artifact: docs/guides/telegram.md. |
365
366
  | `guide.guides-tools-and-web-search` | Tools And Web Search | Bundled Omnius documentation artifact: docs/guides/tools-and-web-search.md. |
366
367
  | `guide.guides-tui-workflows` | TUI Workflows | Bundled Omnius documentation artifact: docs/guides/tui-workflows.md. |
@@ -0,0 +1,85 @@
1
+ # System Tray Indicator
2
+
3
+ Omnius ships a native per-login tray indicator for Linux, macOS, and Windows
4
+ on x64 hosts. It is separate from the REST daemon: closing the indicator never
5
+ stops the daemon, and the daemon remains usable in headless sessions.
6
+
7
+ ## Install And Control
8
+
9
+ ```bash
10
+ omnius tray install # register login startup and start now
11
+ omnius tray status # indicator, health, endpoint, registration
12
+ omnius tray restart
13
+ omnius tray stop # stop only the indicator
14
+ omnius tray uninstall # stop it and remove login startup
15
+ ```
16
+
17
+ The indicator polls the daemon's loopback-only `GET /health` route. It does not
18
+ load a model or contact the configured inference backend. If the daemon uses a
19
+ non-default port, Omnius discovers `OMNIUS_HOST`/`OMNIUS_PORT` from the current
20
+ environment and, on Linux, from the installed user service. Override it
21
+ explicitly when needed:
22
+
23
+ ```bash
24
+ omnius tray install --endpoint http://127.0.0.1:11535
25
+ ```
26
+
27
+ Non-loopback endpoints are rejected. The menu provides the current health and
28
+ version, dashboard and log shortcuts, service-manager-aware daemon controls,
29
+ login-startup control, and **Quit indicator**. Daemon stop is a separate,
30
+ explicit menu action.
31
+
32
+ ## Linux / Ubuntu
33
+
34
+ GNOME needs a StatusNotifier/AppIndicator host. Ubuntu Desktop normally ships
35
+ the `gnome-shell-extension-appindicator` extension; the native helper uses
36
+ Ayatana AppIndicator when available. On a minimal installation:
37
+
38
+ ```bash
39
+ sudo apt install gnome-shell-extension-appindicator libayatana-appindicator3-1
40
+ ```
41
+
42
+ Verify the desktop host without touching the daemon:
43
+
44
+ ```bash
45
+ gdbus call --session \
46
+ --dest org.kde.StatusNotifierWatcher \
47
+ --object-path /StatusNotifierWatcher \
48
+ --method org.freedesktop.DBus.Properties.Get \
49
+ org.kde.StatusNotifierWatcher IsStatusNotifierHostRegistered
50
+ ```
51
+
52
+ Login registration is written to
53
+ `~/.config/autostart/omnius-tray.desktop`. Runtime PID/state stays under
54
+ `$XDG_RUNTIME_DIR/omnius`; logs are under
55
+ `$XDG_STATE_HOME/omnius` (usually `~/.local/state/omnius`). This avoids tying a
56
+ desktop process to the daemon's persistent `~/.omnius` data directory.
57
+
58
+ ## Platform Registration
59
+
60
+ | Platform | Login registration |
61
+ | --- | --- |
62
+ | Linux | XDG autostart desktop entry |
63
+ | macOS | `~/Library/LaunchAgents/ai.omnius.tray.plist` |
64
+ | Windows | per-user Startup-folder command |
65
+
66
+ The optional native helper is exact-pinned and its platform executable is
67
+ checked against a release SHA-256 before execution. Omnius remains usable when
68
+ optional dependencies are disabled; only the tray command reports the missing
69
+ helper. The current helper release targets x64. Other architectures fail with
70
+ an explicit diagnostic instead of falling back to a headless or emulated tray.
71
+
72
+ ## Troubleshooting
73
+
74
+ ```bash
75
+ omnius tray status --json
76
+ tail -f ~/.local/state/omnius/tray.err.log # Linux default
77
+ ```
78
+
79
+ - **No icon on GNOME:** confirm the AppIndicator extension is active and the
80
+ session watcher reports a registered host.
81
+ - **Offline despite a running daemon:** pass the daemon's loopback endpoint
82
+ with `--endpoint`; avoid `0.0.0.0` in client URLs.
83
+ - **No graphical session:** run `omnius tray start` from the logged-in desktop
84
+ session so `DISPLAY`/`WAYLAND_DISPLAY` and the session D-Bus address exist.
85
+
package/docs/index.md CHANGED
@@ -27,6 +27,7 @@ current task.
27
27
  - [Realtime Conversation](./guides/realtime.md)
28
28
  - [Telegram](./guides/telegram.md)
29
29
  - [Media Generation](./guides/media-generation.md)
30
+ - [System Tray Indicator](./guides/system-tray.md)
30
31
  - [Tools And Web Search](./guides/tools-and-web-search.md)
31
32
  - [Categorized OSINT Research](./guides/osint-research.md)
32
33