neoagent 2.3.1-beta.99 → 2.4.1-beta.10

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.
Files changed (101) hide show
  1. package/.env.example +6 -3
  2. package/LICENSE +619 -21
  3. package/README.md +1 -1
  4. package/extensions/chrome-browser/background.mjs +19 -7
  5. package/extensions/chrome-browser/icons/icon128.png +0 -0
  6. package/extensions/chrome-browser/icons/icon16.png +0 -0
  7. package/extensions/chrome-browser/icons/icon48.png +0 -0
  8. package/extensions/chrome-browser/icons/logo.svg +12 -0
  9. package/extensions/chrome-browser/manifest.json +13 -2
  10. package/extensions/chrome-browser/popup.css +5 -0
  11. package/extensions/chrome-browser/popup.html +7 -5
  12. package/extensions/chrome-browser/popup.js +16 -7
  13. package/flutter_app/lib/features/onboarding/onboarding_companion_step.dart +391 -0
  14. package/flutter_app/lib/features/onboarding/onboarding_shell.dart +6 -0
  15. package/flutter_app/lib/features/onboarding/onboarding_welcome_step.dart +1 -1
  16. package/flutter_app/lib/main.dart +2 -0
  17. package/flutter_app/lib/main_controller.dart +156 -3
  18. package/flutter_app/lib/main_devices.dart +485 -119
  19. package/flutter_app/lib/main_integrations.dart +21 -2
  20. package/flutter_app/lib/main_models.dart +60 -0
  21. package/flutter_app/lib/main_settings.dart +289 -30
  22. package/flutter_app/lib/main_theme.dart +31 -2
  23. package/flutter_app/lib/src/backend_client.dart +89 -0
  24. package/flutter_app/lib/src/desktop_companion_actions.dart +144 -0
  25. package/flutter_app/lib/src/desktop_companion_io.dart +145 -4
  26. package/flutter_app/lib/src/desktop_native_bridge.dart +13 -0
  27. package/flutter_app/lib/src/stream_renderer.dart +286 -0
  28. package/flutter_app/macos/Runner/AppDelegate.swift +67 -2
  29. package/flutter_app/macos/Runner/DebugProfile.entitlements +4 -0
  30. package/flutter_app/macos/Runner/Release.entitlements +4 -0
  31. package/flutter_app/pubspec.lock +5 -5
  32. package/lib/manager.js +164 -2
  33. package/package.json +2 -2
  34. package/server/db/database.js +85 -0
  35. package/server/guest_agent.js +19 -1
  36. package/server/http/routes.js +191 -0
  37. package/server/http/socket.js +1 -1
  38. package/server/index.js +4 -1
  39. package/server/public/.last_build_id +1 -1
  40. package/server/public/assets/NOTICES +971 -1066
  41. package/server/public/assets/fonts/MaterialIcons-Regular.otf +0 -0
  42. package/server/public/assets/shaders/ink_sparkle.frag +1 -1
  43. package/server/public/assets/shaders/stretch_effect.frag +1 -1
  44. package/server/public/canvaskit/canvaskit.js +2 -2
  45. package/server/public/canvaskit/canvaskit.js.symbols +11796 -11733
  46. package/server/public/canvaskit/canvaskit.wasm +0 -0
  47. package/server/public/canvaskit/chromium/canvaskit.js +2 -2
  48. package/server/public/canvaskit/chromium/canvaskit.js.symbols +10706 -10643
  49. package/server/public/canvaskit/chromium/canvaskit.wasm +0 -0
  50. package/server/public/canvaskit/experimental_webparagraph/canvaskit.js +171 -0
  51. package/server/public/canvaskit/experimental_webparagraph/canvaskit.js.symbols +9134 -0
  52. package/server/public/canvaskit/experimental_webparagraph/canvaskit.wasm +0 -0
  53. package/server/public/canvaskit/skwasm.js +14 -14
  54. package/server/public/canvaskit/skwasm.js.symbols +12787 -12676
  55. package/server/public/canvaskit/skwasm.wasm +0 -0
  56. package/server/public/canvaskit/skwasm_heavy.js +14 -14
  57. package/server/public/canvaskit/skwasm_heavy.js.symbols +14400 -14286
  58. package/server/public/canvaskit/skwasm_heavy.wasm +0 -0
  59. package/server/public/canvaskit/wimp.js +94 -95
  60. package/server/public/canvaskit/wimp.js.symbols +11325 -11177
  61. package/server/public/canvaskit/wimp.wasm +0 -0
  62. package/server/public/flutter_bootstrap.js +2 -2
  63. package/server/public/main.dart.js +86174 -83144
  64. package/server/routes/browser.js +14 -0
  65. package/server/routes/browser_extension.js +21 -4
  66. package/server/routes/desktop.js +10 -0
  67. package/server/routes/integrations.js +2 -2
  68. package/server/routes/memory.js +73 -0
  69. package/server/routes/settings.js +4 -0
  70. package/server/routes/stream.js +187 -0
  71. package/server/services/ai/engine.js +65 -26
  72. package/server/services/ai/models.js +21 -0
  73. package/server/services/ai/preModelCompaction.js +191 -0
  74. package/server/services/ai/providers/claudeCode.js +273 -0
  75. package/server/services/ai/providers/openaiCodex.js +212 -40
  76. package/server/services/ai/settings.js +12 -2
  77. package/server/services/ai/tools.js +40 -29
  78. package/server/services/android/controller.js +41 -2
  79. package/server/services/browser/controller.js +34 -0
  80. package/server/services/browser/extension/manifest.js +33 -0
  81. package/server/services/browser/extension/provider.js +12 -6
  82. package/server/services/browser/extension/registry.js +188 -18
  83. package/server/services/desktop/gateway.js +28 -3
  84. package/server/services/desktop/protocol.js +34 -0
  85. package/server/services/desktop/provider.js +25 -0
  86. package/server/services/desktop/registry.js +92 -10
  87. package/server/services/integrations/google/provider.js +78 -0
  88. package/server/services/integrations/manager.js +29 -13
  89. package/server/services/manager.js +44 -2
  90. package/server/services/memory/ingestion.js +486 -0
  91. package/server/services/memory/manager.js +422 -0
  92. package/server/services/memory/openhuman_uplift.test.js +98 -0
  93. package/server/services/runtime/backends/local-vm.js +6 -0
  94. package/server/services/runtime/docker-vm-manager.js +26 -3
  95. package/server/services/runtime/manager.js +36 -5
  96. package/server/services/runtime/settings.js +17 -0
  97. package/server/services/streaming/android-stream.js +298 -0
  98. package/server/services/streaming/browser-stream.js +87 -0
  99. package/server/services/streaming/stream-hub.js +231 -0
  100. package/server/services/websocket.js +73 -0
  101. package/server/services/widgets/focus_widget.js +45 -4
@@ -35,6 +35,8 @@ const EVENT_RATE_LIMITS = Object.freeze({
35
35
  'integrations:status': { windowMs: 10 * 1000, max: 20 },
36
36
  'memory:read': { windowMs: 10 * 1000, max: 20 },
37
37
  'memory:search': { windowMs: 10 * 1000, max: 20 },
38
+ 'stream:subscribe': { windowMs: 10 * 1000, max: 40 },
39
+ 'stream:unsubscribe': { windowMs: 10 * 1000, max: 40 },
38
40
  });
39
41
 
40
42
  function asObject(value) {
@@ -54,6 +56,19 @@ function toBoundedInt(value, fallback, min, max) {
54
56
  return Math.min(max, Math.max(min, Math.floor(parsed)));
55
57
  }
56
58
 
59
+ function normalizeStreamPayload(raw) {
60
+ const data = asObject(raw);
61
+ const platform = toOptionalString(data?.platform, 32).toLowerCase() || 'desktop';
62
+ if (!['desktop', 'android', 'browser'].includes(platform)) {
63
+ throw new Error('platform must be desktop, android, or browser.');
64
+ }
65
+ const deviceId = toOptionalString(data?.deviceId || data?.device_id, 256);
66
+ return {
67
+ platform,
68
+ deviceId: deviceId || (platform === 'browser' ? 'browser' : ''),
69
+ };
70
+ }
71
+
57
72
  function resolveAgentFromPayload(userId, value) {
58
73
  const data = asObject(value);
59
74
  return resolveAgentId(userId, data?.agentId || data?.agent_id || null);
@@ -831,9 +846,67 @@ function setupWebSocket(io, services) {
831
846
  }
832
847
  });
833
848
 
849
+ // ── Remote Control Streams ──
850
+
851
+ socket.on('stream:subscribe', (raw) => {
852
+ const limit = allowEvent('stream:subscribe');
853
+ if (!limit.allowed) {
854
+ recordRateLimitHit(rateLimitObserver, userId, socket.id, 'stream:subscribe', limit.retryAfterMs);
855
+ return socket.emit('stream:error', {
856
+ error: `Rate limit exceeded for stream:subscribe. Retry in ${Math.ceil(limit.retryAfterMs / 1000)}s.`,
857
+ });
858
+ }
859
+ try {
860
+ const streamHub = services.streamHub || services.app?.locals?.streamHub;
861
+ if (!streamHub) throw new Error('Stream hub is unavailable.');
862
+ const data = normalizeStreamPayload(raw);
863
+ if (!data.deviceId) throw new Error('deviceId is required.');
864
+ const subscriberCount = streamHub.subscribe(userId, data.deviceId, data.platform, socket.id);
865
+ socket.emit('stream:subscribed', {
866
+ platform: data.platform,
867
+ deviceId: data.deviceId,
868
+ subscriberCount,
869
+ });
870
+ } catch (err) {
871
+ console.error(`[WS] stream:subscribe failed for user ${userId}:`, err);
872
+ socket.emit('stream:error', { error: sanitizeError(err) });
873
+ }
874
+ });
875
+
876
+ socket.on('stream:unsubscribe', async (raw) => {
877
+ const limit = allowEvent('stream:unsubscribe');
878
+ if (!limit.allowed) {
879
+ recordRateLimitHit(rateLimitObserver, userId, socket.id, 'stream:unsubscribe', limit.retryAfterMs);
880
+ return socket.emit('stream:error', {
881
+ error: `Rate limit exceeded for stream:unsubscribe. Retry in ${Math.ceil(limit.retryAfterMs / 1000)}s.`,
882
+ });
883
+ }
884
+ try {
885
+ const streamHub = services.streamHub || services.app?.locals?.streamHub;
886
+ if (!streamHub) throw new Error('Stream hub is unavailable.');
887
+ const data = normalizeStreamPayload(raw);
888
+ if (!data.deviceId) throw new Error('deviceId is required.');
889
+ const subscriberCount = await streamHub.unsubscribe(userId, data.platform, data.deviceId, socket.id);
890
+ socket.emit('stream:unsubscribed', {
891
+ platform: data.platform,
892
+ deviceId: data.deviceId,
893
+ subscriberCount,
894
+ });
895
+ } catch (err) {
896
+ console.error(`[WS] stream:unsubscribe failed for user ${userId}:`, err);
897
+ socket.emit('stream:error', { error: sanitizeError(err) });
898
+ }
899
+ });
900
+
834
901
  // ── Disconnect ──
835
902
 
836
903
  socket.on('disconnect', () => {
904
+ const streamHub = services.streamHub || services.app?.locals?.streamHub;
905
+ if (streamHub && typeof streamHub.unsubscribeAll === 'function') {
906
+ void streamHub.unsubscribeAll(socket.id).catch((err) => {
907
+ console.error(`[WS] Failed to unsubscribe streams for socket ${socket.id}:`, err);
908
+ });
909
+ }
837
910
  if (!voiceRuntimeManager || typeof voiceRuntimeManager.closeSession !== 'function') {
838
911
  socket.data.voiceSessionIds?.clear?.();
839
912
  console.log(`[WS] User ${userId} disconnected (${socket.id})`);
@@ -52,6 +52,18 @@ function buildAssistantFocusSnapshot(memoryManager, userId, agentId) {
52
52
  ).all(userId, scopedAgentId);
53
53
  const conversations = memoryManager?.getRecentConversations?.(userId, 4, { agentId: scopedAgentId }) || [];
54
54
  const memories = memoryManager?.listMemories?.(userId, { limit: 6, agentId: scopedAgentId }) || [];
55
+ const recentKnowledgeChanges = memoryManager?.listRecentKnowledgeChanges?.(userId, {
56
+ agentId: scopedAgentId,
57
+ limit: 5,
58
+ }) || [];
59
+ const knowledgeViews = memoryManager?.listKnowledgeViews?.(userId, {
60
+ agentId: scopedAgentId,
61
+ limit: 4,
62
+ }) || [];
63
+ const ingestionOverview = memoryManager?.getIngestionOverview?.(userId, {
64
+ agentId: scopedAgentId,
65
+ limit: 8,
66
+ }) || [];
55
67
 
56
68
  const activeThreads = conversations.slice(0, 3).map((conversation) => ({
57
69
  title: safeTrim(conversation.title, 80),
@@ -75,6 +87,24 @@ function buildAssistantFocusSnapshot(memoryManager, userId, agentId) {
75
87
  value: safeTrim(run.status || 'unknown', 32),
76
88
  }));
77
89
  const rememberedContext = memories.slice(0, 2).map((memory) => safeTrim(memory.content, 140));
90
+ const backgroundAwareness = {
91
+ summary: safeTrim(
92
+ recentKnowledgeChanges[0]?.summary
93
+ || recentKnowledgeChanges[0]?.title
94
+ || knowledgeViews[0]?.summary
95
+ || '',
96
+ 180,
97
+ ),
98
+ changedCount: recentKnowledgeChanges.length,
99
+ lastChangedAt: recentKnowledgeChanges[0]?.updatedAt || null,
100
+ };
101
+ const syncHealth = ingestionOverview.slice(0, 3).map((source) => ({
102
+ label: safeTrim(source.providerKey || source.sourceTypes?.[0] || 'Background context', 80),
103
+ value: safeTrim(source.status || 'ready', 32),
104
+ lastRefreshAt: source.lastRefreshAt || null,
105
+ nextRefreshAt: source.nextRefreshAt || null,
106
+ documentCount: Number(source.documentCount || 0),
107
+ }));
78
108
 
79
109
  const currentFocus = safeTrim(
80
110
  selfState.focus?.currentFocus
@@ -90,6 +120,10 @@ function buildAssistantFocusSnapshot(memoryManager, userId, agentId) {
90
120
  nearTermPriorities,
91
121
  recentSignals,
92
122
  rememberedContext,
123
+ recentKnowledgeChanges,
124
+ knowledgeViews,
125
+ backgroundAwareness,
126
+ syncHealth,
93
127
  assistantIdentity: {
94
128
  name: safeTrim(selfState.identity?.displayName, 80),
95
129
  style: safeTrim(selfState.identity?.style, 120),
@@ -99,11 +133,15 @@ function buildAssistantFocusSnapshot(memoryManager, userId, agentId) {
99
133
  }
100
134
 
101
135
  function buildAssistantFocusWidgetPayload(snapshot) {
136
+ const knowledgeChangeCount = Number(snapshot.backgroundAwareness?.changedCount || 0);
137
+ const primaryMetric = knowledgeChangeCount > 0 ? knowledgeChangeCount : snapshot.activeThreads.length;
102
138
  const rows = [
103
139
  ...snapshot.nearTermPriorities,
104
140
  ...snapshot.recentSignals,
141
+ ...snapshot.syncHealth,
105
142
  ].slice(0, 3);
106
143
  const chips = [
144
+ ...snapshot.recentKnowledgeChanges.map((item) => item.title),
107
145
  ...snapshot.activeThreads.map((item) => item.title),
108
146
  ...snapshot.rememberedContext,
109
147
  ].filter(Boolean).slice(0, 3);
@@ -113,14 +151,17 @@ function buildAssistantFocusWidgetPayload(snapshot) {
113
151
  kicker: 'Assistant state',
114
152
  subtitle: snapshot.currentFocus,
115
153
  body: snapshot.activeThreads[0]?.preview
154
+ || snapshot.backgroundAwareness?.summary
116
155
  || snapshot.rememberedContext[0]
117
156
  || 'Watching recent conversations, tasks, and runs.',
118
- metric: String(snapshot.activeThreads.length),
119
- metricLabel: snapshot.activeThreads.length === 1 ? 'active thread' : 'active threads',
157
+ metric: String(primaryMetric),
158
+ metricLabel: knowledgeChangeCount > 0
159
+ ? (knowledgeChangeCount === 1 ? 'knowledge change' : 'knowledge changes')
160
+ : (snapshot.activeThreads.length === 1 ? 'active thread' : 'active threads'),
120
161
  secondaryMetric: String(snapshot.nearTermPriorities.length),
121
162
  secondaryLabel: 'priorities',
122
- tertiaryMetric: formatRelativeTimestamp(snapshot.generatedAt),
123
- tertiaryLabel: 'updated',
163
+ tertiaryMetric: formatRelativeTimestamp(snapshot.backgroundAwareness?.lastChangedAt || snapshot.generatedAt),
164
+ tertiaryLabel: snapshot.backgroundAwareness?.lastChangedAt ? 'last change' : 'updated',
124
165
  rows,
125
166
  chips,
126
167
  iconToken: 'focus',