aimeat 1.16.2 → 1.18.0

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 (281) hide show
  1. package/dist/.env.example +6 -1
  2. package/dist/locales/en.json +166 -4
  3. package/dist/locales/fi.json +166 -4
  4. package/dist/public/components/Card.js +17 -3
  5. package/dist/public/components/Collapsible.js +39 -0
  6. package/dist/public/components/CopyButton.js +55 -24
  7. package/dist/public/components/DataTable.js +57 -0
  8. package/dist/public/components/EmptyState.js +28 -0
  9. package/dist/public/components/KeyValueRow.js +37 -0
  10. package/dist/public/components/Modal.js +22 -5
  11. package/dist/public/components/Pagination.js +70 -0
  12. package/dist/public/components/SearchBar.js +44 -0
  13. package/dist/public/components/StatusDot.js +29 -0
  14. package/dist/public/components/TagList.js +53 -0
  15. package/dist/public/components/ToggleSwitch.js +28 -0
  16. package/dist/public/components/index.js +41 -1
  17. package/dist/public/css/components/tags.css +24 -0
  18. package/dist/public/css/style-testbed.css +135 -0
  19. package/dist/public/css/theme.css +556 -34
  20. package/dist/public/css/views/admin-agent-integration.css +2 -33
  21. package/dist/public/css/views/admin.css +730 -686
  22. package/dist/public/css/views/agents-detail.css +402 -14
  23. package/dist/public/css/views/help.css +2 -1
  24. package/dist/public/css/views/hobbies.css +53 -33
  25. package/dist/public/css/views/marketplace.css +73 -60
  26. package/dist/public/css/views/openclaw.css +12 -6
  27. package/dist/public/css/views/portal-classic.css +34 -26
  28. package/dist/public/css/views/portal-dev.css +41 -32
  29. package/dist/public/css/views/portal.css +14 -18
  30. package/dist/public/css/views/profile.css +252 -164
  31. package/dist/public/css/views/public-knowledge-viewer.css +18 -6
  32. package/dist/public/css/views/scheduler.css +100 -0
  33. package/dist/public/js/format.js +41 -0
  34. package/dist/public/js/services/agent-tasks.js +14 -1
  35. package/dist/public/js/services/generator-spec-validate.js +5 -1
  36. package/dist/public/js/services/schedules.js +53 -0
  37. package/dist/public/js/style-testbed.js +170 -0
  38. package/dist/public/js/theme.js +114 -0
  39. package/dist/public/spa.html +74 -16
  40. package/dist/public/style-testbed.html +420 -0
  41. package/dist/public/views/admin/agent-integration-tab.js +23 -38
  42. package/dist/public/views/admin/agent-tasks-tab.js +14 -21
  43. package/dist/public/views/admin/chat-instances-tab.js +1 -1
  44. package/dist/public/views/admin/config-tab.js +7 -2
  45. package/dist/public/views/admin/cortex-tab.js +20 -14
  46. package/dist/public/views/admin/csm-tab.js +22 -18
  47. package/dist/public/views/admin/directory-tab.js +3 -3
  48. package/dist/public/views/admin/email-tab.js +23 -28
  49. package/dist/public/views/admin/federation-tab.js +15 -5
  50. package/dist/public/views/admin/generator-debug-tab.js +10 -8
  51. package/dist/public/views/admin/genesis-tab.js +4 -4
  52. package/dist/public/views/admin/ghii-tab.js +3 -3
  53. package/dist/public/views/admin/knowledge-tab.js +15 -5
  54. package/dist/public/views/admin/matching-tab.js +3 -3
  55. package/dist/public/views/admin/memory-tab.js +16 -20
  56. package/dist/public/views/admin/msm-tab.js +30 -17
  57. package/dist/public/views/admin/overview-tab.js +7 -7
  58. package/dist/public/views/admin/packages-tab.js +10 -9
  59. package/dist/public/views/admin/portal-tab.js +14 -4
  60. package/dist/public/views/admin/prompts-tab.js +5 -5
  61. package/dist/public/views/admin/push-tab.js +15 -4
  62. package/dist/public/views/admin/realtime-tab.js +3 -3
  63. package/dist/public/views/admin/services-tab.js +18 -27
  64. package/dist/public/views/admin/shared.js +48 -58
  65. package/dist/public/views/admin/sharing-groups-tab.js +5 -10
  66. package/dist/public/views/admin/stats-tab.js +3 -3
  67. package/dist/public/views/guides.js +9 -13
  68. package/dist/public/views/help.js +3 -24
  69. package/dist/public/views/hobbies.js +47 -41
  70. package/dist/public/views/marketplace.js +42 -34
  71. package/dist/public/views/openclaw.js +0 -1
  72. package/dist/public/views/portal-classic.js +4 -15
  73. package/dist/public/views/portal-dev.js +28 -30
  74. package/dist/public/views/portal.js +16 -21
  75. package/dist/public/views/portfolio.js +11 -12
  76. package/dist/public/views/profile/access-tab.js +250 -1
  77. package/dist/public/views/profile/agents/agent-card.js +75 -4
  78. package/dist/public/views/profile/agents/tab-activity.js +15 -13
  79. package/dist/public/views/profile/agents/tab-agent-config.js +60 -6
  80. package/dist/public/views/profile/agents/tab-integration.js +37 -60
  81. package/dist/public/views/profile/agents/tab-messages.js +10 -1
  82. package/dist/public/views/profile/agents/tab-quality.js +25 -19
  83. package/dist/public/views/profile/agents/tab-schedules.js +110 -0
  84. package/dist/public/views/profile/agents-activity-subtab.js +4 -4
  85. package/dist/public/views/profile/agents-services-subtab.js +5 -4
  86. package/dist/public/views/profile/agents-tab.js +141 -37
  87. package/dist/public/views/profile/agents-tasks-subtab.js +221 -26
  88. package/dist/public/views/profile/calibrator-batch.js +5 -11
  89. package/dist/public/views/profile/calibrator-tab.js +4 -9
  90. package/dist/public/views/profile/chat-sessions-tab.js +9 -5
  91. package/dist/public/views/profile/email-tab.js +2 -2
  92. package/dist/public/views/profile/extensions-tab.js +27 -22
  93. package/dist/public/views/profile/federation-tab.js +4 -1
  94. package/dist/public/views/profile/foundry-detail.js +22 -23
  95. package/dist/public/views/profile/foundry-tab.js +12 -13
  96. package/dist/public/views/profile/generator-dashboard/DebugPanel.js +4 -7
  97. package/dist/public/views/profile/generator-dashboard/use-dashboard-core.js +4 -1
  98. package/dist/public/views/profile/generator-dashboard/use-edit-mode.js +3 -2
  99. package/dist/public/views/profile/generator-detail.js +26 -30
  100. package/dist/public/views/profile/generator-tab.js +5 -5
  101. package/dist/public/views/profile/knowledge-tab.js +12 -12
  102. package/dist/public/views/profile/landing-page.js +100 -149
  103. package/dist/public/views/profile/memory-tab.js +15 -11
  104. package/dist/public/views/profile/node-stats-tab.js +3 -3
  105. package/dist/public/views/profile/nodes-tab.js +10 -3
  106. package/dist/public/views/profile/notifications-tab.js +3 -3
  107. package/dist/public/views/profile/packages-tab.js +14 -14
  108. package/dist/public/views/profile/scheduler-tab.js +340 -0
  109. package/dist/public/views/profile/services-tab.js +1 -1
  110. package/dist/public/views/profile/shared.js +16 -12
  111. package/dist/public/views/profile/wallet-tab.js +27 -50
  112. package/dist/public/views/profile/work-tab.js +29 -31
  113. package/dist/public/views/profile.js +24 -0
  114. package/dist/public/views/public-knowledge-viewer.js +9 -18
  115. package/dist/src/auth/middleware.d.ts +3 -2
  116. package/dist/src/auth/middleware.d.ts.map +1 -1
  117. package/dist/src/auth/middleware.js +78 -3
  118. package/dist/src/auth/middleware.js.map +1 -1
  119. package/dist/src/cli/init-wizard.d.ts.map +1 -1
  120. package/dist/src/cli/init-wizard.js +13 -1
  121. package/dist/src/cli/init-wizard.js.map +1 -1
  122. package/dist/src/config.d.ts +6 -0
  123. package/dist/src/config.d.ts.map +1 -1
  124. package/dist/src/config.js +12 -1
  125. package/dist/src/config.js.map +1 -1
  126. package/dist/src/generated/api-types.d.ts +708 -8
  127. package/dist/src/generated/api-types.d.ts.map +1 -1
  128. package/dist/src/mcp/agent-schedules.d.ts +23 -0
  129. package/dist/src/mcp/agent-schedules.d.ts.map +1 -0
  130. package/dist/src/mcp/agent-schedules.js +175 -0
  131. package/dist/src/mcp/agent-schedules.js.map +1 -0
  132. package/dist/src/mcp/annotations.d.ts.map +1 -1
  133. package/dist/src/mcp/annotations.js +6 -0
  134. package/dist/src/mcp/annotations.js.map +1 -1
  135. package/dist/src/mcp/catalog/definitions.d.ts.map +1 -1
  136. package/dist/src/mcp/catalog/definitions.js +55 -0
  137. package/dist/src/mcp/catalog/definitions.js.map +1 -1
  138. package/dist/src/mcp/catalog/scopes.d.ts.map +1 -1
  139. package/dist/src/mcp/catalog/scopes.js +12 -0
  140. package/dist/src/mcp/catalog/scopes.js.map +1 -1
  141. package/dist/src/mcp/catalog/surfaces.d.ts.map +1 -1
  142. package/dist/src/mcp/catalog/surfaces.js +2 -0
  143. package/dist/src/mcp/catalog/surfaces.js.map +1 -1
  144. package/dist/src/mcp/index.d.ts.map +1 -1
  145. package/dist/src/mcp/index.js +2 -0
  146. package/dist/src/mcp/index.js.map +1 -1
  147. package/dist/src/models/agent-task-schemas.d.ts +6 -0
  148. package/dist/src/models/agent-task-schemas.d.ts.map +1 -1
  149. package/dist/src/models/agent-task-schemas.js +6 -0
  150. package/dist/src/models/agent-task-schemas.js.map +1 -1
  151. package/dist/src/models/schemas.d.ts +1 -0
  152. package/dist/src/models/schemas.d.ts.map +1 -1
  153. package/dist/src/models/schemas.js +4 -0
  154. package/dist/src/models/schemas.js.map +1 -1
  155. package/dist/src/routes/access-tokens.d.ts +17 -0
  156. package/dist/src/routes/access-tokens.d.ts.map +1 -0
  157. package/dist/src/routes/access-tokens.js +164 -0
  158. package/dist/src/routes/access-tokens.js.map +1 -0
  159. package/dist/src/routes/agent-tasks.d.ts +2 -0
  160. package/dist/src/routes/agent-tasks.d.ts.map +1 -1
  161. package/dist/src/routes/agent-tasks.js +118 -23
  162. package/dist/src/routes/agent-tasks.js.map +1 -1
  163. package/dist/src/routes/agents.d.ts.map +1 -1
  164. package/dist/src/routes/agents.js +46 -0
  165. package/dist/src/routes/agents.js.map +1 -1
  166. package/dist/src/routes/ai.d.ts +6 -9
  167. package/dist/src/routes/ai.d.ts.map +1 -1
  168. package/dist/src/routes/ai.js +26 -175
  169. package/dist/src/routes/ai.js.map +1 -1
  170. package/dist/src/routes/apps.d.ts.map +1 -1
  171. package/dist/src/routes/apps.js +104 -34
  172. package/dist/src/routes/apps.js.map +1 -1
  173. package/dist/src/routes/auth.d.ts.map +1 -1
  174. package/dist/src/routes/auth.js +77 -4
  175. package/dist/src/routes/auth.js.map +1 -1
  176. package/dist/src/routes/cortex.d.ts +2 -1
  177. package/dist/src/routes/cortex.d.ts.map +1 -1
  178. package/dist/src/routes/cortex.js +13 -8
  179. package/dist/src/routes/cortex.js.map +1 -1
  180. package/dist/src/routes/extensions.d.ts.map +1 -1
  181. package/dist/src/routes/extensions.js +85 -51
  182. package/dist/src/routes/extensions.js.map +1 -1
  183. package/dist/src/routes/generator.d.ts.map +1 -1
  184. package/dist/src/routes/generator.js +109 -43
  185. package/dist/src/routes/generator.js.map +1 -1
  186. package/dist/src/routes/ghii.d.ts.map +1 -1
  187. package/dist/src/routes/ghii.js +34 -13
  188. package/dist/src/routes/ghii.js.map +1 -1
  189. package/dist/src/routes/libs.d.ts +15 -0
  190. package/dist/src/routes/libs.d.ts.map +1 -1
  191. package/dist/src/routes/libs.js +178 -55
  192. package/dist/src/routes/libs.js.map +1 -1
  193. package/dist/src/routes/portal.d.ts.map +1 -1
  194. package/dist/src/routes/portal.js +1 -0
  195. package/dist/src/routes/portal.js.map +1 -1
  196. package/dist/src/routes/schedules.d.ts +33 -0
  197. package/dist/src/routes/schedules.d.ts.map +1 -0
  198. package/dist/src/routes/schedules.js +380 -0
  199. package/dist/src/routes/schedules.js.map +1 -0
  200. package/dist/src/server-bootstrap/config-init.js +2 -2
  201. package/dist/src/server-bootstrap/config-init.js.map +1 -1
  202. package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
  203. package/dist/src/server-bootstrap/routes-loader.js +7 -0
  204. package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
  205. package/dist/src/server-bootstrap/service-init.d.ts.map +1 -1
  206. package/dist/src/server-bootstrap/service-init.js +2 -1
  207. package/dist/src/server-bootstrap/service-init.js.map +1 -1
  208. package/dist/src/services/access-token.d.ts +28 -0
  209. package/dist/src/services/access-token.d.ts.map +1 -0
  210. package/dist/src/services/access-token.js +37 -0
  211. package/dist/src/services/access-token.js.map +1 -0
  212. package/dist/src/services/ai-completion.d.ts +83 -0
  213. package/dist/src/services/ai-completion.d.ts.map +1 -0
  214. package/dist/src/services/ai-completion.js +187 -0
  215. package/dist/src/services/ai-completion.js.map +1 -0
  216. package/dist/src/services/config-schema.d.ts.map +1 -1
  217. package/dist/src/services/config-schema.js +3 -0
  218. package/dist/src/services/config-schema.js.map +1 -1
  219. package/dist/src/services/generator-prompt-seeds.d.ts.map +1 -1
  220. package/dist/src/services/generator-prompt-seeds.js +6 -3
  221. package/dist/src/services/generator-prompt-seeds.js.map +1 -1
  222. package/dist/src/services/generator-prompts/spec-validate.d.ts.map +1 -1
  223. package/dist/src/services/generator-prompts/spec-validate.js +6 -2
  224. package/dist/src/services/generator-prompts/spec-validate.js.map +1 -1
  225. package/dist/src/services/generator-registration.d.ts +12 -4
  226. package/dist/src/services/generator-registration.d.ts.map +1 -1
  227. package/dist/src/services/generator-registration.js +71 -0
  228. package/dist/src/services/generator-registration.js.map +1 -1
  229. package/dist/src/services/generator-validate.d.ts +10 -0
  230. package/dist/src/services/generator-validate.d.ts.map +1 -1
  231. package/dist/src/services/generator-validate.js +180 -36
  232. package/dist/src/services/generator-validate.js.map +1 -1
  233. package/dist/src/services/owner-session.d.ts +43 -0
  234. package/dist/src/services/owner-session.d.ts.map +1 -0
  235. package/dist/src/services/owner-session.js +175 -0
  236. package/dist/src/services/owner-session.js.map +1 -0
  237. package/dist/src/services/schedule-constraints.d.ts +65 -0
  238. package/dist/src/services/schedule-constraints.d.ts.map +1 -0
  239. package/dist/src/services/schedule-constraints.js +106 -0
  240. package/dist/src/services/schedule-constraints.js.map +1 -0
  241. package/dist/src/services/scheduler.d.ts +34 -0
  242. package/dist/src/services/scheduler.d.ts.map +1 -1
  243. package/dist/src/services/scheduler.js +266 -22
  244. package/dist/src/services/scheduler.js.map +1 -1
  245. package/dist/src/services/skill-bundle/generator.js +19 -0
  246. package/dist/src/services/skill-bundle/generator.js.map +1 -1
  247. package/dist/src/storage/interface.d.ts +48 -4
  248. package/dist/src/storage/interface.d.ts.map +1 -1
  249. package/dist/src/storage/interface.js.map +1 -1
  250. package/dist/src/storage/providers/mongodb/index.d.ts +31 -0
  251. package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
  252. package/dist/src/storage/providers/mongodb/index.js +187 -9
  253. package/dist/src/storage/providers/mongodb/index.js.map +1 -1
  254. package/dist/src/storage/providers/sqlite/index.d.ts +31 -0
  255. package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
  256. package/dist/src/storage/providers/sqlite/index.js +131 -8
  257. package/dist/src/storage/providers/sqlite/index.js.map +1 -1
  258. package/dist/src/storage/providers/sqlite/repos/agent-task.d.ts.map +1 -1
  259. package/dist/src/storage/providers/sqlite/repos/agent-task.js +6 -4
  260. package/dist/src/storage/providers/sqlite/repos/agent-task.js.map +1 -1
  261. package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
  262. package/dist/src/storage/providers/sqlite/schema.js +79 -8
  263. package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
  264. package/dist/src/storage/repositories/pat.repository.d.ts +40 -0
  265. package/dist/src/storage/repositories/pat.repository.d.ts.map +1 -0
  266. package/dist/src/storage/repositories/pat.repository.js +14 -0
  267. package/dist/src/storage/repositories/pat.repository.js.map +1 -0
  268. package/dist/src/storage/repositories/scheduler.repository.d.ts +2 -0
  269. package/dist/src/storage/repositories/scheduler.repository.d.ts.map +1 -1
  270. package/dist/src/storage/repositories/session.repository.d.ts +51 -0
  271. package/dist/src/storage/repositories/session.repository.d.ts.map +1 -1
  272. package/dist/src/storage/repositories/session.repository.js +12 -0
  273. package/dist/src/storage/repositories/session.repository.js.map +1 -1
  274. package/dist/src/utils/env-config.d.ts.map +1 -1
  275. package/dist/src/utils/env-config.js +25 -1
  276. package/dist/src/utils/env-config.js.map +1 -1
  277. package/dist/src/utils/env-validator.d.ts.map +1 -1
  278. package/dist/src/utils/env-validator.js +4 -0
  279. package/dist/src/utils/env-validator.js.map +1 -1
  280. package/package.json +1 -1
  281. package/prisma/schema.prisma +48 -0
@@ -10,6 +10,8 @@
10
10
  * v1.3.0 -- 2026-05-21 -- Shorten agent prompt to delegate to tier1; add Download/Copy Instructions buttons
11
11
  * v1.4.0 -- 2026-05-21 -- Add sub-tab navigation (Tasks, Directives) in expanded agent detail view
12
12
  * v1.5.0 -- 2026-05-22 -- Add Capabilities sub-tab with technical/domain skill display
13
+ * v1.6.0 -- 2026-06-02 -- Component unification (#2): scope modal uses the canonical
14
+ * <Modal> component (className="scope-modal" preserves width; adds Escape/✕ close)
13
15
  * v1.6.0 -- 2026-05-22 -- Add Activity sub-tab with stats, chart, scheduled jobs, event log
14
16
  * v1.7.0 -- 2026-05-22 -- Add Services and Messages sub-tabs
15
17
  * v2.1.0 -- 2026-05-24 -- Fix: scroll-to on board click, agent count badge in header
@@ -29,22 +31,28 @@
29
31
  * v3.2.0 -- 2026-05-29 -- Tag filter bar + group-by toggle (none / tag / mode)
30
32
  * v3.3.0 -- 2026-05-29 -- Add "Connect a task-runner" collapsible with CrewAI-shaped paste prompt
31
33
  * v3.4.0 -- 2026-05-31 -- Drag-to-reorder agent bars (per-browser localStorage order, ungrouped/unfiltered list only) + pop-out button opening an agent in its own window (/v1/profile?solo=)
34
+ * v3.5.0 -- 2026-06-02 -- Component unification: replace 4 bespoke copy-prompt buttons
35
+ * (connect command, MCP onboarding, manual agent prompt, task-runner prompt) with the
36
+ * canonical <CopyButton> (className="copy-prompt-btn" preserves appearance); removed the
37
+ * now-dead copiedAction state + markCopied helper.
32
38
  */
33
39
  import { h } from 'preact';
34
40
  import { useState, useEffect, useRef } from 'preact/hooks';
35
41
  import htm from 'htm';
36
42
  const html = htm.bind(h);
37
43
  import { t } from '/js/i18n.js';
38
- import { escHtml, copyToClipboard } from '/js/utils.js';
44
+ import { escHtml } from '/js/utils.js';
45
+ import { CopyButton } from '/components/CopyButton.js';
39
46
  import { Spinner } from './shared.js';
40
47
  import { apiGet, apiPost, apiPatch } from '/js/api.js';
41
48
  import { listAgents, updateAgentScopes, deleteAgent } from '/js/services/agents.js';
42
49
  import { getNodeUrl } from '/js/services/auth.js';
43
- import { useConfirm } from '/components/Modal.js';
50
+ import { Modal, useConfirm } from '/components/Modal.js';
44
51
  import SharedBoard from './agents/shared-board.js';
45
52
  import AgentCard from './agents/agent-card.js';
46
53
  import { getOnboarding } from '/js/services/agent-integration.js';
47
54
  import { listTasks } from '/js/services/agent-tasks.js';
55
+ import { listMessages } from '/js/services/agent-messages.js';
48
56
 
49
57
  // === Scope Management Constants ===
50
58
  const SCOPE_DOMAINS = [
@@ -58,6 +66,12 @@ const SCOPE_DOMAINS = [
58
66
  { key: 'catalogue', permissions: ['read'] },
59
67
  { key: 'generator', permissions: ['read', 'write', 'execute'] },
60
68
  { key: 'task', permissions: ['read', 'write', 'manage'] },
69
+ // cortex:write — install/activate/deactivate/delete cortex libraries (browser-side UI)
70
+ // ext:write — activate/deactivate/delete extensions, manage instances (server-side WASM)
71
+ // Note: extension INSTALL is requireAuth-only at the route (agents can push code that stays
72
+ // inert until ext:write activates it). Cortex INSTALL requires cortex:write.
73
+ { key: 'cortex', permissions: ['write'] },
74
+ { key: 'ext', permissions: ['write'] },
61
75
  ];
62
76
 
63
77
  const SCOPE_TEMPLATES = {
@@ -359,6 +373,47 @@ function saveAgentOrder(owner, names) {
359
373
  try { localStorage.setItem(ORDER_KEY_PREFIX + owner, JSON.stringify(names)); }
360
374
  catch { /* ignore quota/availability errors */ }
361
375
  }
376
+
377
+ // ── Per-browser "last seen per tab" state (localStorage) ──
378
+ // Drives the per-agent change badges: how many tasks / messages / memory
379
+ // entries have updated since the owner last opened that tab. Per-browser, keyed
380
+ // by owner — same rationale as the agent ordering above (no server round-trip;
381
+ // "what's new since I looked HERE" is inherently a per-device affordance).
382
+ // Shape: { [agentName]: { tasks: iso, messages: iso, memory: iso } }
383
+ // The three keys map to the Tasks, Messages and Memory (data-access) tabs.
384
+ const SEEN_KEY_PREFIX = 'aimeat-agent-seen:';
385
+ function loadSeen(owner) {
386
+ if (!owner) return {};
387
+ try { return JSON.parse(localStorage.getItem(SEEN_KEY_PREFIX + owner) || '{}') || {}; }
388
+ catch { return {}; }
389
+ }
390
+ function saveSeen(owner, data) {
391
+ if (!owner) return;
392
+ try { localStorage.setItem(SEEN_KEY_PREFIX + owner, JSON.stringify(data)); }
393
+ catch { /* ignore quota/availability errors */ }
394
+ }
395
+ // Stamp the given agent+tab as seen-now and persist. Read-modify-write against
396
+ // the freshest localStorage so a concurrent loadData() seed doesn't clobber it.
397
+ function markTabSeen(owner, agentName, tab) {
398
+ if (!owner || !agentName || !tab) return;
399
+ const seen = loadSeen(owner);
400
+ (seen[agentName] ||= {})[tab] = new Date().toISOString();
401
+ saveSeen(owner, seen);
402
+ }
403
+ // Count items whose newest available timestamp (first present of `fields`) is
404
+ // strictly newer than `since`. `since` falsy → 0 (the caller seeds a baseline
405
+ // on first observation, so an agent's whole history is never dumped as "new").
406
+ function countNewer(items, since, fields) {
407
+ if (!Array.isArray(items) || !since) return 0;
408
+ const sinceMs = new Date(since).getTime();
409
+ let n = 0;
410
+ for (const it of items) {
411
+ let ts = null;
412
+ for (const f of fields) { if (it && it[f]) { ts = it[f]; break; } }
413
+ if (ts && new Date(ts).getTime() > sinceMs) n++;
414
+ }
415
+ return n;
416
+ }
362
417
  // Effective ordering: agents named in the saved order first (in that order),
363
418
  // then any agents not yet ordered (e.g. newly connected) in their API order.
364
419
  function effectiveOrderedNames(agents, order) {
@@ -382,7 +437,6 @@ export default function AgentsTab({ session, showToast, onStats }) {
382
437
  const { confirm, ConfirmUI } = useConfirm();
383
438
  const [agents, setAgents] = useState(null);
384
439
  const [onboardings, setOnboardings] = useState({});
385
- const [copiedAction, setCopiedAction] = useState(null);
386
440
  const [platExpand, setPlatExpand] = useState(false);
387
441
  const [activePlat, setActivePlat] = useState('windows');
388
442
  const [scopesModal, setScopesModal] = useState(null);
@@ -395,6 +449,10 @@ export default function AgentsTab({ session, showToast, onStats }) {
395
449
  const [taskRunnerExpanded, setTaskRunnerExpanded] = useState(false);
396
450
  const [taskRunnerName, setTaskRunnerName] = useState('');
397
451
  const [taskStatsMap, setTaskStatsMap] = useState({});
452
+ // Per-agent unseen-change counts { name: { tasks, messages, memory } } driving
453
+ // the collapsed mini-badge + per-tab number badges. Computed in loadData()
454
+ // from the localStorage "last seen per tab" baseline (see loadSeen/countNewer).
455
+ const [changesMap, setChangesMap] = useState({});
398
456
  const [tagFilter, setTagFilter] = useState(new Set());
399
457
  const [groupBy, setGroupBy] = useState('none'); // 'none' | 'tag' | 'mode'
400
458
  // Per-browser drag-to-reorder of the agent bars (localStorage-backed).
@@ -430,9 +488,17 @@ export default function AgentsTab({ session, showToast, onStats }) {
430
488
  onDragEnd: () => { draggedName.current = null; setDraggingName(null); },
431
489
  };
432
490
 
433
- const markCopied = (action) => {
434
- setCopiedAction(action);
435
- setTimeout(() => setCopiedAction(current => current === action ? null : current), 2000);
491
+ // Owner opened a tab on an agent → stamp it seen and clear that badge now.
492
+ // The next loadData() recomputes from the same baseline, keeping it at 0
493
+ // until a new change actually arrives.
494
+ const handleTabSeen = (agentName, tab) => {
495
+ if (!session) return;
496
+ markTabSeen(session.owner, agentName, tab);
497
+ setChangesMap(prev => {
498
+ const cur = prev[agentName];
499
+ if (!cur || !cur[tab]) return prev;
500
+ return { ...prev, [agentName]: { ...cur, [tab]: 0 } };
501
+ });
436
502
  };
437
503
 
438
504
  useEffect(() => {
@@ -468,19 +534,56 @@ export default function AgentsTab({ session, showToast, onStats }) {
468
534
  }));
469
535
  setOnboardings(obMap);
470
536
  const tsMap = {};
537
+ const seen = loadSeen(session.owner);
538
+ let seenSeeded = false;
539
+ const chMap = {};
471
540
  await Promise.all(list.map(async (a) => {
472
541
  try {
473
- const [doneResp, activeResp] = await Promise.all([
542
+ const [doneResp, activeResp, msgResp, memResp] = await Promise.all([
474
543
  listTasks(a.name, { status: 'done', per_page: 100 }),
475
544
  listTasks(a.name, { status: 'active', per_page: 100 }),
545
+ listMessages(a.name, { perPage: 100 }).catch(() => null),
546
+ apiGet(`/v1/memory?prefix=&per_page=100&agent=${encodeURIComponent(a.gaii || a.name)}`).catch(() => null),
476
547
  ]);
477
548
  const today = new Date().toISOString().slice(0, 10);
478
- const doneToday = (doneResp?.data?.tasks || []).filter(tk => tk.completedAt?.startsWith(today)).length;
479
- const activeCount = (activeResp?.data?.tasks || []).length;
480
- tsMap[a.name] = { done: doneToday, active: activeCount };
481
- } catch { tsMap[a.name] = null; }
549
+ const doneTasks = doneResp?.data?.tasks || [];
550
+ const activeTasks = activeResp?.data?.tasks || [];
551
+ const doneToday = doneTasks.filter(tk => tk.completedAt?.startsWith(today)).length;
552
+ tsMap[a.name] = { done: doneToday, active: activeTasks.length };
553
+
554
+ // Per-tab unseen-change counts. First observation of a tab seeds its
555
+ // baseline to "now" (count 0) so an agent's whole history is never
556
+ // dumped as new; only changes after that point raise the badge.
557
+ const agentSeen = seen[a.name] || (seen[a.name] = {});
558
+ const nowIso = new Date().toISOString();
559
+ const ch = {};
560
+ for (const [tab, items, fields] of [
561
+ ['tasks', [...doneTasks, ...activeTasks], ['updatedAt', 'completedAt', 'createdAt']],
562
+ // Only count agent→owner messages as unread; the owner's own
563
+ // inbound messages are not "new" to them.
564
+ ['messages', (msgResp?.data?.messages || []).filter(m => m.direction !== 'inbound'), ['createdAt', 'created_at']],
565
+ ['memory', (memResp?.data?.items || memResp?.data || []), ['updated_at', 'updatedAt', 'created_at', 'createdAt']],
566
+ ]) {
567
+ if (agentSeen[tab] === undefined) { agentSeen[tab] = nowIso; seenSeeded = true; ch[tab] = 0; }
568
+ else ch[tab] = countNewer(items, agentSeen[tab], fields);
569
+ }
570
+ chMap[a.name] = ch;
571
+ } catch { tsMap[a.name] = null; chMap[a.name] = null; }
482
572
  }));
483
573
  setTaskStatsMap(tsMap);
574
+ // Persist any freshly-seeded baselines. Merge against the latest storage
575
+ // so a tab the user opened mid-fetch (markTabSeen) is not overwritten.
576
+ if (seenSeeded) {
577
+ const fresh = loadSeen(session.owner);
578
+ for (const name of Object.keys(seen)) {
579
+ const merged = fresh[name] || (fresh[name] = {});
580
+ for (const tab of Object.keys(seen[name])) {
581
+ if (merged[tab] === undefined) merged[tab] = seen[name][tab];
582
+ }
583
+ }
584
+ saveSeen(session.owner, fresh);
585
+ }
586
+ setChangesMap(chMap);
484
587
  } catch { setAgents([]); }
485
588
  }
486
589
 
@@ -643,11 +746,11 @@ export default function AgentsTab({ session, showToast, onStats }) {
643
746
 
644
747
  <p class="mb-half text-bold">${t('profile.agents.cliInstall')}</p>
645
748
  <div class="agent-prompt-box"><code>npx aimeat connect --url ${getNodeUrl()} --owner ${session.owner}</code></div>
646
- <button class="copy-prompt-btn" onClick=${() => {
647
- copyToClipboard(`npx aimeat connect --url ${getNodeUrl()} --owner ${session.owner}`).then(() => {
648
- markCopied('connect-command');
649
- });
650
- }}>${copiedAction === 'connect-command' ? '\u2705 ' + t('profile.agents.copied') : t('profile.agents.copyCommand')}</button>
749
+ <${CopyButton}
750
+ text=${`npx aimeat connect --url ${getNodeUrl()} --owner ${session.owner}`}
751
+ className="copy-prompt-btn"
752
+ label=${t('profile.agents.copyCommand')}
753
+ copiedLabel=${'\u2705 ' + t('profile.agents.copied')} />
651
754
 
652
755
  <p class="mt-1 mb-half text-bold">${t('profile.agents.cliServe')}</p>
653
756
  <div class="agent-prompt-box"><code>npx aimeat connect serve</code></div>
@@ -657,11 +760,11 @@ export default function AgentsTab({ session, showToast, onStats }) {
657
760
  <p class="mt-1 mb-half text-bold">${t('profile.agents.agentInstructionTitle')}</p>
658
761
  <p class="text-caption mb-half">${t('profile.agents.agentInstructionDesc')}</p>
659
762
  <div class="agent-prompt-box">${buildMcpOnboardingPrompt()}</div>
660
- <button class="copy-prompt-btn" onClick=${() => {
661
- copyToClipboard(buildMcpOnboardingPrompt()).then(() => {
662
- markCopied('agent-instruction');
663
- });
664
- }}>${copiedAction === 'agent-instruction' ? '\u2705 ' + t('profile.agents.copied') : t('profile.agents.copyAgentInstruction')}</button>
763
+ <${CopyButton}
764
+ text=${buildMcpOnboardingPrompt()}
765
+ className="copy-prompt-btn"
766
+ label=${t('profile.agents.copyAgentInstruction')}
767
+ copiedLabel=${'\u2705 ' + t('profile.agents.copied')} />
665
768
 
666
769
  <div class="pf-agent-divider mt-1">
667
770
  <button class="expand-btn" onClick=${() => setPlatExpand(!platExpand)}>
@@ -690,11 +793,11 @@ export default function AgentsTab({ session, showToast, onStats }) {
690
793
  <div class="mt-half">
691
794
  <p class="text-caption mb-half">${t('profile.agents.pasteDesc')}</p>
692
795
  <div class="agent-prompt-box">${buildAgentPrompt(session)}</div>
693
- <button class="copy-prompt-btn" onClick=${() => {
694
- copyToClipboard(buildAgentPrompt(session)).then(() => {
695
- markCopied('manual-agent-prompt');
696
- });
697
- }}>${copiedAction === 'manual-agent-prompt' ? '\u2705 ' + t('profile.agents.copied') : t('profile.agents.copyPrompt')}</button>
796
+ <${CopyButton}
797
+ text=${buildAgentPrompt(session)}
798
+ className="copy-prompt-btn"
799
+ label=${t('profile.agents.copyPrompt')}
800
+ copiedLabel=${'\u2705 ' + t('profile.agents.copied')} />
698
801
  </div>
699
802
  `}
700
803
  </div>
@@ -718,11 +821,11 @@ export default function AgentsTab({ session, showToast, onStats }) {
718
821
  onInput=${(e) => setTaskRunnerName(e.target.value)} />
719
822
  </div>
720
823
  <div class="agent-prompt-box">${buildTaskRunnerPrompt(session, taskRunnerName)}</div>
721
- <button class="copy-prompt-btn" onClick=${() => {
722
- copyToClipboard(buildTaskRunnerPrompt(session, taskRunnerName)).then(() => {
723
- markCopied('task-runner-prompt');
724
- });
725
- }}>${copiedAction === 'task-runner-prompt' ? '\u2705 ' + t('profile.agents.copied') : t('profile.agents.taskRunner.copyButton')}</button>
824
+ <${CopyButton}
825
+ text=${buildTaskRunnerPrompt(session, taskRunnerName)}
826
+ className="copy-prompt-btn"
827
+ label=${t('profile.agents.taskRunner.copyButton')}
828
+ copiedLabel=${'\u2705 ' + t('profile.agents.copied')} />
726
829
  </div>
727
830
  `}
728
831
  </div>
@@ -750,6 +853,8 @@ export default function AgentsTab({ session, showToast, onStats }) {
750
853
  groupBy,
751
854
  onboardings,
752
855
  taskStatsMap,
856
+ changesMap,
857
+ onTabSeen: handleTabSeen,
753
858
  expandedAgent,
754
859
  toggleAgent,
755
860
  session,
@@ -825,7 +930,7 @@ function renderFilterBar(agents, tagFilter, setTagFilter, groupBy, setGroupBy) {
825
930
  `;
826
931
  }
827
932
 
828
- function renderAgentGroups({ agents, tagFilter, groupBy, onboardings, taskStatsMap, expandedAgent, toggleAgent, session, showToast, setScopesModal, handleDeleteAgent, toggleFederate, onPopOut, dnd, agentOrder }) {
933
+ function renderAgentGroups({ agents, tagFilter, groupBy, onboardings, taskStatsMap, changesMap, onTabSeen, expandedAgent, toggleAgent, session, showToast, setScopesModal, handleDeleteAgent, toggleFederate, onPopOut, dnd, agentOrder }) {
829
934
  // Tag filter: agent must have ALL selected tags (intersection)
830
935
  const filtered = tagFilter.size === 0
831
936
  ? agents
@@ -848,6 +953,8 @@ function renderAgentGroups({ agents, tagFilter, groupBy, onboardings, taskStatsM
848
953
  session=${session}
849
954
  showToast=${showToast}
850
955
  allAgents=${agents}
956
+ changes=${changesMap?.[a.name] || null}
957
+ onTabSeen=${onTabSeen}
851
958
  onScopesClick=${(agent) => setScopesModal(agent)}
852
959
  onDeleteClick=${handleDeleteAgent}
853
960
  onFederateToggle=${toggleFederate}
@@ -1014,9 +1121,7 @@ function ScopesModal({ agent, session, onSave, onCancel }) {
1014
1121
  const isReadOnly = !(session.roles?.includes('owner') || session.roles?.includes('operator'));
1015
1122
 
1016
1123
  return html`
1017
- <div class="modal-overlay" onClick=${e => { if (e.target.className.includes('modal-overlay')) onCancel(); }}>
1018
- <div class="modal scope-modal">
1019
- <h3>${t('profile.agents.scopeUi.scopeProfile')}: ${escHtml(agent.display_name || agent.name)}</h3>
1124
+ <${Modal} open=${true} onClose=${onCancel} className="scope-modal" title=${`${t('profile.agents.scopeUi.scopeProfile')}: ${agent.display_name || agent.name}`}>
1020
1125
  <div class="scope-agent-info">${escHtml(agent.gaii || '')}</div>
1021
1126
 
1022
1127
  ${isReadOnly ? html`
@@ -1083,6 +1188,5 @@ function ScopesModal({ agent, session, onSave, onCancel }) {
1083
1188
  <button class="btn-outline" onClick=${onCancel}>${t('profile.agents.scopeUi.cancel')}</button>
1084
1189
  </div>
1085
1190
  `}
1086
- </div>
1087
- </div>`;
1191
+ <//>`;
1088
1192
  }