aimeat 1.28.0 → 1.29.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 (68) hide show
  1. package/dist/locales/en.json +46 -1
  2. package/dist/locales/fi.json +46 -1
  3. package/dist/public/css/views/agents-detail.css +11 -0
  4. package/dist/public/css/views/profile.css +71 -10
  5. package/dist/public/js/services/memory.js +62 -4
  6. package/dist/public/js/services/organisms.js +49 -3
  7. package/dist/public/js/services/workflows.js +4 -3
  8. package/dist/public/spa.html +3 -0
  9. package/dist/public/views/command-palette.js +145 -0
  10. package/dist/public/views/profile/knowledge-tab.js +6 -9
  11. package/dist/public/views/profile/landing-page.js +7 -29
  12. package/dist/public/views/profile/memory-tab.js +251 -26
  13. package/dist/public/views/profile/organisms/agents.js +6 -11
  14. package/dist/public/views/profile/organisms/home.js +1 -1
  15. package/dist/public/views/profile/organisms/members.js +9 -5
  16. package/dist/public/views/profile/organisms/mindmap.js +157 -59
  17. package/dist/public/views/profile/organisms/panels.js +36 -19
  18. package/dist/public/views/profile/organisms/timeline-panel.js +30 -1
  19. package/dist/public/views/profile/organisms/workspace-comments.js +15 -5
  20. package/dist/public/views/profile/organisms/workspace-list.js +35 -23
  21. package/dist/public/views/profile/organisms/workspace.js +166 -31
  22. package/dist/public/views/profile/organisms-tab.js +17 -5
  23. package/dist/public/views/profile/workflows-tab.js +6 -6
  24. package/dist/src/cli/connect/mcp/local-server.d.ts.map +1 -1
  25. package/dist/src/cli/connect/mcp/local-server.js +28 -0
  26. package/dist/src/cli/connect/mcp/local-server.js.map +1 -1
  27. package/dist/src/generated/api-types.d.ts +333 -10
  28. package/dist/src/generated/api-types.d.ts.map +1 -1
  29. package/dist/src/routes/agent-tasks.d.ts.map +1 -1
  30. package/dist/src/routes/agent-tasks.js +13 -0
  31. package/dist/src/routes/agent-tasks.js.map +1 -1
  32. package/dist/src/routes/memory.d.ts +3 -0
  33. package/dist/src/routes/memory.d.ts.map +1 -1
  34. package/dist/src/routes/memory.js +232 -11
  35. package/dist/src/routes/memory.js.map +1 -1
  36. package/dist/src/routes/organisms.d.ts.map +1 -1
  37. package/dist/src/routes/organisms.js +321 -3
  38. package/dist/src/routes/organisms.js.map +1 -1
  39. package/dist/src/routes/workflows.d.ts.map +1 -1
  40. package/dist/src/routes/workflows.js +13 -2
  41. package/dist/src/routes/workflows.js.map +1 -1
  42. package/dist/src/services/connect-tunnel.d.ts +11 -0
  43. package/dist/src/services/connect-tunnel.d.ts.map +1 -1
  44. package/dist/src/services/connect-tunnel.js +36 -0
  45. package/dist/src/services/connect-tunnel.js.map +1 -1
  46. package/dist/src/services/organism-search.d.ts +12 -7
  47. package/dist/src/services/organism-search.d.ts.map +1 -1
  48. package/dist/src/services/organism-search.js +93 -60
  49. package/dist/src/services/organism-search.js.map +1 -1
  50. package/dist/src/services/workspace-enrichment.d.ts +76 -0
  51. package/dist/src/services/workspace-enrichment.d.ts.map +1 -0
  52. package/dist/src/services/workspace-enrichment.js +144 -0
  53. package/dist/src/services/workspace-enrichment.js.map +1 -0
  54. package/dist/src/storage/providers/mongodb/index.d.ts +1 -0
  55. package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
  56. package/dist/src/storage/providers/mongodb/index.js +2 -0
  57. package/dist/src/storage/providers/mongodb/index.js.map +1 -1
  58. package/dist/src/storage/providers/sqlite/index.d.ts +1 -0
  59. package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
  60. package/dist/src/storage/providers/sqlite/index.js +4 -0
  61. package/dist/src/storage/providers/sqlite/index.js.map +1 -1
  62. package/dist/src/storage/providers/sqlite/repos/memory.d.ts +1 -0
  63. package/dist/src/storage/providers/sqlite/repos/memory.d.ts.map +1 -1
  64. package/dist/src/storage/providers/sqlite/repos/memory.js +4 -0
  65. package/dist/src/storage/providers/sqlite/repos/memory.js.map +1 -1
  66. package/dist/src/storage/repositories/memory.repository.d.ts +1 -0
  67. package/dist/src/storage/repositories/memory.repository.d.ts.map +1 -1
  68. package/package.json +1 -1
@@ -2055,7 +2055,36 @@
2055
2055
  "bulkClear": "Clear selection",
2056
2056
  "bulkDone": "{n} updated",
2057
2057
  "bulkDeleted": "{n} deleted",
2058
- "bulkDeleteConfirm": "Delete {n} memory entries? This cannot be undone."
2058
+ "bulkDeleteConfirm": "Delete {n} memory entries? This cannot be undone.",
2059
+ "toolsLabel": "Tools",
2060
+ "storageUsed": "Storage",
2061
+ "keysWord": "keys",
2062
+ "sizeLabel": "Value size",
2063
+ "sortSize": "Largest first",
2064
+ "loadingValue": "Loading value…",
2065
+ "loadContents": "Load all contents",
2066
+ "searchContents": "Search content or key…",
2067
+ "searchResultCount": "{n} matches",
2068
+ "searchEmpty": "No matches",
2069
+ "searchClear": "Clear search",
2070
+ "searchInGroup": "Search in this group",
2071
+ "searchInGroupHint": "Type a query to search within {g}",
2072
+ "exportBtn": "Export",
2073
+ "exportDone": "Exported {n} entries",
2074
+ "importBtn": "Import",
2075
+ "importModeLabel": "Conflict handling",
2076
+ "importMode": {
2077
+ "skip": "Skip existing",
2078
+ "overwrite": "Overwrite",
2079
+ "rename": "Import as new"
2080
+ },
2081
+ "importConfirm": "Import {n} entries? Existing keys are handled by mode: {mode}.",
2082
+ "importDone": "Imported: {c} new, {u} updated, {s} skipped",
2083
+ "importBadJson": "Not a valid JSON backup",
2084
+ "importEmpty": "No entries found in file",
2085
+ "deleteGroup": "Delete group",
2086
+ "deleteGroupConfirm": "Delete all {n} entries under \"{g}\"? This cannot be undone.",
2087
+ "deleteGroupUnsupported": "This group cannot be bulk-deleted"
2059
2088
  },
2060
2089
  "files": {
2061
2090
  "title": "Stored Files",
@@ -6306,6 +6335,10 @@
6306
6335
  "mindmap": {
6307
6336
  "titleOrg": "Organism map",
6308
6337
  "titleWs": "Workspace map",
6338
+ "chart": "Chart",
6339
+ "chartMindmap": "Mindmap",
6340
+ "chartLr": "Flowchart →",
6341
+ "chartTd": "Flowchart ↓",
6309
6342
  "level": "Level",
6310
6343
  "levelWs": "Workspaces",
6311
6344
  "levelSpaces": "+ Spaces",
@@ -6323,6 +6356,18 @@
6323
6356
  "pick": "Pick a point to see the structure then.",
6324
6357
  "changed": "structure changed"
6325
6358
  },
6359
+ "search": {
6360
+ "wsPlaceholder": "Search this workspace…",
6361
+ "orgPlaceholder": "Search this organism…",
6362
+ "clear": "Clear",
6363
+ "noMatches": "No matches",
6364
+ "paletteTitle": "Search everything",
6365
+ "palettePlaceholder": "Search organisms, workspaces, records…",
6366
+ "recents": "Recent",
6367
+ "results": "Results",
6368
+ "openHint": "↵ open · Esc close",
6369
+ "searching": "Searching…"
6370
+ },
6326
6371
  "organisms": {
6327
6372
  "title": "Organisms",
6328
6373
  "backToList": "All organisms",
@@ -2055,7 +2055,36 @@
2055
2055
  "bulkClear": "Tyhjennä valinta",
2056
2056
  "bulkDone": "{n} päivitetty",
2057
2057
  "bulkDeleted": "{n} poistettu",
2058
- "bulkDeleteConfirm": "Poistetaanko {n} muistimerkintää? Ei voi perua."
2058
+ "bulkDeleteConfirm": "Poistetaanko {n} muistimerkintää? Ei voi perua.",
2059
+ "toolsLabel": "Työkalut",
2060
+ "storageUsed": "Tallennustila",
2061
+ "keysWord": "avainta",
2062
+ "sizeLabel": "Arvon koko",
2063
+ "sortSize": "Suurimmat ensin",
2064
+ "loadingValue": "Ladataan sisältöä…",
2065
+ "loadContents": "Lataa kaikki sisällöt",
2066
+ "searchContents": "Hae sisällöstä tai avaimesta…",
2067
+ "searchResultCount": "{n} osumaa",
2068
+ "searchEmpty": "Ei osumia",
2069
+ "searchClear": "Tyhjennä haku",
2070
+ "searchInGroup": "Hae tästä ryhmästä",
2071
+ "searchInGroupHint": "Kirjoita hakusana hakeaksesi ryhmästä {g}",
2072
+ "exportBtn": "Vie",
2073
+ "exportDone": "Vietiin {n} merkintää",
2074
+ "importBtn": "Tuo",
2075
+ "importModeLabel": "Ristiriitojen käsittely",
2076
+ "importMode": {
2077
+ "skip": "Ohita olemassa olevat",
2078
+ "overwrite": "Korvaa",
2079
+ "rename": "Tuo uusina"
2080
+ },
2081
+ "importConfirm": "Tuodaanko {n} merkintää? Olemassa olevat avaimet käsitellään tilassa: {mode}.",
2082
+ "importDone": "Tuotiin: {c} uutta, {u} päivitetty, {s} ohitettu",
2083
+ "importBadJson": "Ei kelvollinen JSON-varmuuskopio",
2084
+ "importEmpty": "Tiedostosta ei löytynyt merkintöjä",
2085
+ "deleteGroup": "Poista ryhmä",
2086
+ "deleteGroupConfirm": "Poistetaanko kaikki {n} merkintää ryhmästä \"{g}\"? Ei voi perua.",
2087
+ "deleteGroupUnsupported": "Tätä ryhmää ei voi poistaa joukkona"
2059
2088
  },
2060
2089
  "files": {
2061
2090
  "title": "Tallennetut tiedostot",
@@ -6306,6 +6335,10 @@
6306
6335
  "mindmap": {
6307
6336
  "titleOrg": "Organismin kartta",
6308
6337
  "titleWs": "Työtilan kartta",
6338
+ "chart": "Kaavio",
6339
+ "chartMindmap": "Mindmap",
6340
+ "chartLr": "Vuokaavio →",
6341
+ "chartTd": "Vuokaavio ↓",
6309
6342
  "level": "Taso",
6310
6343
  "levelWs": "Työtilat",
6311
6344
  "levelSpaces": "+ Spacet",
@@ -6323,6 +6356,18 @@
6323
6356
  "pick": "Valitse kohta nähdäksesi rakenteen silloin.",
6324
6357
  "changed": "rakenne muuttui"
6325
6358
  },
6359
+ "search": {
6360
+ "wsPlaceholder": "Hae tästä työtilasta…",
6361
+ "orgPlaceholder": "Hae tästä organismista…",
6362
+ "clear": "Tyhjennä",
6363
+ "noMatches": "Ei osumia",
6364
+ "paletteTitle": "Hae kaikkea",
6365
+ "palettePlaceholder": "Hae organismeja, työtiloja, recordeja…",
6366
+ "recents": "Viimeisimmät",
6367
+ "results": "Tulokset",
6368
+ "openHint": "↵ avaa · Esc sulje",
6369
+ "searching": "Haetaan…"
6370
+ },
6326
6371
  "organisms": {
6327
6372
  "title": "Organismit",
6328
6373
  "backToList": "Kaikki organismit",
@@ -4,6 +4,9 @@
4
4
  * Uses agd- prefix to avoid collisions with other profile styles.
5
5
  * All colors use CSS variables from theme.css.
6
6
  * @version-history
7
+ * v1.12.0 -- 2026-06-22 -- Add .pf-agd-capwrap rule so the capabilities summary
8
+ * ("N tools · N skills · N langs") gets the card content inset (1rem) instead of
9
+ * sitting flush against the edge, plus a small top gap from the tag/badge strip.
7
10
  * v1.11.0 -- 2026-06-21 -- Add has-unseen DOT badge modifier (.pf-agd-*-badge--dot): a small
8
11
  * round indicator replacing the old numeric change-count badge.
9
12
  * v1.10.0 -- 2026-06-03 -- Add fleet "running now" panel styles (.pf-agd-active,
@@ -1796,6 +1799,14 @@
1796
1799
  color: var(--text-dim, #6B7280);
1797
1800
  }
1798
1801
 
1802
+ /* Capabilities summary (collapsed one-liner "N tools · N skills · N langs").
1803
+ The wrapper carries the card's content inset (1rem) so the summary doesn't sit
1804
+ flush against the card edge, plus a little top gap to separate it from the
1805
+ tag/badge strip above. */
1806
+ .pf .pf-agd-capwrap {
1807
+ padding: 0.4rem 1rem 0.1rem;
1808
+ }
1809
+
1799
1810
  /* Capabilities badges */
1800
1811
  .pf .pf-agd-capabilities {
1801
1812
  display: flex;
@@ -4,6 +4,9 @@
4
4
  * wallet, memory, agents, extensions, packages, knowledge, generator, and
5
5
  * edit panels. Consumes theme.css tokens so it flips with dark/light mode.
6
6
  * @version-history
7
+ * v2.6.0 — 2026-06-22 — Scalable Memory tab: .pf-mem-quota* (storage-usage bar), .pf-mem-size
8
+ * (value-size column), .mem-tools-bar/.mem-import-group (server search + export/import row),
9
+ * .mem-group-actions (hover search/delete on group headers), .mem-search-summary.
7
10
  * v2.5.0 — 2026-06-16 — Grouped workspace nav: .pj-org-groups/.pj-org-group/.pj-org-group-cap
8
11
  * (clickable group caption, -static variant)/.pj-org-group-tabs cluster the tab row, with a
9
12
  * vertical divider (border-left) between groups; stacked-group content .pj-group-view/
@@ -273,6 +276,23 @@
273
276
  .pf .mem-vis{font-size:.7rem}
274
277
  .pf .shield-icon{font-size:.85rem;cursor:pointer;opacity:.6;transition:opacity .2s}
275
278
  .pf .shield-icon:hover{opacity:1}
279
+ /* ── Scalable Memory tab (2026-06-22): storage bar, size column, tools bar, group actions ── */
280
+ .pf .pf-mem-quota{margin:.25rem 0 .6rem}
281
+ .pf .pf-mem-quota-row{display:flex;justify-content:space-between;align-items:center;margin-bottom:.25rem}
282
+ .pf .pf-mem-quota-bar{height:6px;border-radius:999px;background:var(--bg-dim);overflow:hidden}
283
+ .pf .pf-mem-quota-fill{height:100%;background:var(--accent);border-radius:999px;transition:width .3s}
284
+ .pf .pf-mem-quota-fill--danger{background:var(--danger)}
285
+ .pf .pf-mem-size{font-size:.68rem;color:var(--muted);font-variant-numeric:tabular-nums;white-space:nowrap;flex-shrink:0}
286
+ /* Data-management tools grouped in their own bordered panel, above the search/sort rows */
287
+ .pf .mem-tools-section{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap;padding:.5rem .75rem;margin:.25rem 0 .6rem;background:var(--bg-surface,var(--bg-dim));border:1px solid var(--border);border-radius:8px}
288
+ .pf .mem-tools-label{font-size:.68rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--text-dim)}
289
+ .pf .mem-tools-actions{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}
290
+ .pf .mem-import-group{display:inline-flex;align-items:center;gap:.35rem}
291
+ .pf .mem-bottom-bar{gap:.75rem}
292
+ .pf .mem-bottom-bar .mem-new-btn{margin-left:auto}
293
+ .pf .mem-group-actions{margin-left:auto;display:inline-flex;align-items:center;gap:.15rem;opacity:0;transition:opacity .15s}
294
+ .pf .mem-group-header:hover .mem-group-actions{opacity:1}
295
+ .pf .mem-search-summary{display:flex;justify-content:space-between;align-items:center;padding:.35rem .2rem;border-bottom:1px solid var(--border);margin-bottom:.25rem}
276
296
  .pf .key-rules-box{background:var(--bg-surface);border:1px solid rgba(139,92,246,.3);border-radius:8px;padding:.75rem;margin-top:.75rem;margin-bottom:.5rem}
277
297
  .pf .mem-detail{background:rgba(0,0,0,.04);border:1px solid rgba(232,86,74,.1);border-radius:8px;padding:1rem;margin-top:.5rem;font-size:.85rem}
278
298
  .pf .mem-detail pre{white-space:pre-wrap;word-break:break-all;font-family:monospace;color:var(--text,#1A1A2E);font-size:.8rem}
@@ -2152,10 +2172,14 @@
2152
2172
  .pj-item .btn-ghost { background: var(--card); box-shadow: var(--shadow-sm); }
2153
2173
  .pj-item .btn-ghost:hover { background: var(--bg-surface); border-color: var(--text-muted); }
2154
2174
  .pj-item-text { flex: 1; font-size: 0.9rem; }
2155
- .pj-rec { border-radius: 6px; }
2175
+ /* Each record is its own card with air around it, so stacked records read as separate units. */
2176
+ .pj-rec { border: 1px solid var(--border); background: var(--card); border-radius: 8px; padding: 0.4rem 0.7rem; margin: 0.5rem 0; }
2156
2177
  .pj-rec-title { flex: 1; text-align: left; background: none; border: none; cursor: pointer; font-size: 0.9rem; color: var(--text); padding: 0; }
2157
2178
  .pj-rec-title:hover { color: var(--accent); }
2158
- .pj-rec-fields { margin: 0.1rem 0 0.4rem 0.6rem; padding: 0.2rem 0.6rem; border-left: 2px solid var(--border); background: var(--bg-dim); border-radius: 0 6px 6px 0; }
2179
+ .pj-rec-fields { margin: 0.4rem 0 0.2rem; padding: 0.5rem 0.7rem; border-top: 1px solid var(--border); background: var(--bg-dim); border-radius: 6px; }
2180
+ /* Inline editor — replaces the field view in the SAME card while editing (one editor at a time). */
2181
+ .pj-rec-edit { margin: 0.4rem 0 0.2rem; padding: 0.5rem 0.7rem; border-top: 1px solid var(--border); background: var(--bg-dim); border-radius: 6px; }
2182
+ .pj-rec-edit-new { border-top: none; }
2159
2183
  /* Comment thread on a workspace object (record/document) */
2160
2184
  .pj-comments { margin: 0.5rem 0 0.25rem; padding-top: 0.5rem; border-top: 1px solid var(--border); }
2161
2185
  .pj-comment { padding: 0.4rem 0.5rem; margin: 0.3rem 0; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; }
@@ -2165,14 +2189,45 @@
2165
2189
  .pj-comment-actions { display: flex; gap: 0.3rem; }
2166
2190
  .pj-comment-compose { display: flex; flex-direction: column; gap: 0.35rem; margin-top: 0.5rem; }
2167
2191
  .pj-comment-compose textarea { resize: vertical; }
2168
- /* Dotted leader between each field label and its value ties the two ends of a
2169
- long row together so you can follow which value belongs to which key. */
2170
- .pj-rec-fields .kv-row { align-items: baseline; gap: 0; }
2171
- .pj-rec-fields .kv-label { flex: 1 1 auto; display: flex; align-items: baseline; min-width: 0; color: var(--muted); }
2172
- .pj-rec-fields .kv-label::after { content: ""; flex: 1 1 auto; height: 0; margin: 0 0.45rem; border-bottom: 1px dotted var(--border); transform: translateY(-0.18em); }
2173
- .pj-rec-fields .kv-value { flex: 0 1 auto; text-align: right; }
2192
+ /* Record fields: label ON TOP (small, muted), value below at full width, LEFT-aligned and wrapping
2193
+ natural left-to-right reading (was a right-aligned single line that ran off and didn't wrap). */
2194
+ .pj-rec-field { margin: 0 0 0.6rem; }
2195
+ .pj-rec-field:last-child { margin-bottom: 0; }
2196
+ .pj-rec-field-label { font-size: 0.72rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.02em; color: var(--muted); margin-bottom: 0.12rem; }
2197
+ .pj-rec-field-val { font-size: 0.88rem; color: var(--text); text-align: left; overflow-wrap: anywhere; }
2198
+ .pj-rec-field-text { white-space: pre-wrap; }
2199
+ .pj-rec-field-list { margin: 0.1rem 0 0; padding-left: 1.1rem; }
2200
+ .pj-rec-field-list li { margin: 0.1rem 0; }
2201
+ .pj-rec-json { margin: 0.1rem 0 0; padding: 0.4rem 0.55rem; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; font-size: 0.78rem; white-space: pre-wrap; overflow-wrap: anywhere; max-height: 18rem; overflow: auto; }
2202
+ /* Markdown inside a record field — tighten the first/last margins so it sits flush in the field. */
2203
+ .pj-rec-md .md-body { font-size: 0.88rem; line-height: 1.5; }
2204
+ .pj-rec-md .md-body > :first-child { margin-top: 0; }
2205
+ .pj-rec-md .md-body > :last-child { margin-bottom: 0; }
2174
2206
  .pj-rec-empty { font-size: 0.82rem; padding: 0.2rem 0; }
2175
2207
  .pj-empty { color: var(--muted); font-size: 0.85rem; padding: 0.2rem 0; }
2208
+
2209
+ /* ── In-context content search (Kerros 1): filter tabs to matches + grouped result list ── */
2210
+ .pj-ws-searchbar { display: flex; align-items: center; gap: 0.5rem; margin: 0.4rem 0 0.6rem; }
2211
+ .pj-ws-searchbar .search-bar { flex: 1; }
2212
+ .pj-org-tab-match { background: var(--accent); color: #fff; }
2213
+ .pj-search-results { margin-top: 0.4rem; }
2214
+ .pj-search-group { margin: 0 0 0.9rem; }
2215
+ .pj-search-group-head { display: flex; align-items: center; gap: 0.4rem; font-size: 0.8rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.02em; color: var(--text-dim); margin: 0 0 0.35rem; padding-bottom: 0.25rem; border-bottom: 1px solid var(--border); }
2216
+ .pj-search-hit { display: flex; flex-direction: column; gap: 0.15rem; width: 100%; text-align: left; background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 0.5rem 0.7rem; margin: 0.35rem 0; cursor: pointer; }
2217
+ .pj-search-hit:hover { border-color: var(--accent); background: var(--bg-dim); }
2218
+ .pj-search-hit-title { font-size: 0.9rem; font-weight: 600; color: var(--text); }
2219
+ .pj-search-hit-snippet { font-size: 0.8rem; color: var(--muted); overflow-wrap: anywhere; }
2220
+
2221
+ /* ── Global command palette (Cmd-K, Kerros 2) ── */
2222
+ .cmdk { display: flex; flex-direction: column; gap: 0.4rem; min-width: min(92vw, 40rem); }
2223
+ .cmdk-hint { font-size: 0.72rem; text-align: right; }
2224
+ .cmdk-list { max-height: 50vh; overflow: auto; margin-top: 0.2rem; }
2225
+ .cmdk-group { font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); margin: 0.6rem 0 0.2rem; }
2226
+ .cmdk-item { display: flex; flex-direction: column; gap: 0.1rem; width: 100%; text-align: left; background: none; border: 1px solid transparent; border-radius: 8px; padding: 0.45rem 0.6rem; cursor: pointer; color: var(--text); }
2227
+ .cmdk-item.is-sel, .cmdk-item:hover { background: var(--bg-dim); border-color: var(--border); }
2228
+ .cmdk-item-label { font-size: 0.9rem; font-weight: 600; }
2229
+ .cmdk-item-sub { font-size: 0.75rem; color: var(--muted); }
2230
+ .cmdk-item-snippet { font-size: 0.76rem; color: var(--text-dim); overflow-wrap: anywhere; max-height: 2.6em; overflow: hidden; }
2176
2231
  .pj-decision { font-size: 0.85rem; color: var(--muted); }
2177
2232
  .pj-field { display: flex; flex-direction: column; gap: 0.2rem; margin-bottom: 0.35rem; }
2178
2233
  .pj-field > span { font-size: 0.75rem; color: var(--muted); }
@@ -2560,13 +2615,19 @@
2560
2615
  .pj-mm-opt select { width: auto; }
2561
2616
  .pj-mm-check { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.8rem; color: var(--text-dim); cursor: pointer; }
2562
2617
  .pj-mindmap-canvas { overflow: auto; max-height: 36rem; }
2563
- .pj-mindmap-canvas .node { cursor: pointer; }
2564
- .pj-mindmap-canvas .node:hover { opacity: 0.82; }
2618
+ .pj-mindmap-canvas .node, .pj-mindmap-canvas .mindmap-node { cursor: pointer; }
2619
+ .pj-mindmap-canvas .node:hover, .pj-mindmap-canvas .mindmap-node:hover { opacity: 0.82; }
2565
2620
  .pj-mindmap-hint { margin-top: 0.4rem; }
2621
+ /* Mindmap heatmap: `:::heatN` attaches the class to the node; colour its shape from theme vars
2622
+ (flowchart modes colour via Mermaid classDef with the same theme-derived values). */
2623
+ .pj-mindmap-canvas .heat1 rect, .pj-mindmap-canvas .heat1 path, .pj-mindmap-canvas .heat1 circle { fill: var(--success-bg); stroke: var(--success-fg); }
2624
+ .pj-mindmap-canvas .heat2 rect, .pj-mindmap-canvas .heat2 path, .pj-mindmap-canvas .heat2 circle { fill: var(--warn-bg); stroke: var(--warn-fg); }
2625
+ .pj-mindmap-canvas .heat3 rect, .pj-mindmap-canvas .heat3 path, .pj-mindmap-canvas .heat3 circle { fill: var(--danger-bg); stroke: var(--danger-fg); }
2566
2626
 
2567
2627
  /* ── Structure timeline / development (Osa D) ── */
2568
2628
  .pj-timeline { margin: 0.4rem 0 0.9rem; }
2569
2629
  .pj-timeline-body { margin-top: 0.4rem; padding: 0.75rem 0.9rem; background: var(--card); border: 1px solid var(--border); border-radius: 10px; }
2630
+ .pj-timeline-diagram { overflow: auto; max-height: 22rem; margin-bottom: 0.8rem; padding-bottom: 0.6rem; border-bottom: 1px solid var(--border); }
2570
2631
  .pj-timeline-grid { display: grid; grid-template-columns: minmax(14rem, 22rem) 1fr; gap: 1rem; align-items: start; }
2571
2632
  .pj-timeline-list { list-style: none; margin: 0; padding: 0; max-height: 30rem; overflow: auto; }
2572
2633
  .pj-timeline-item { margin: 0; }
@@ -5,6 +5,8 @@
5
5
  * v1.1.0 — 2026-06-10 — Add getMemory(key) single-entry read (GET /v1/memory/:key).
6
6
  * v1.2.0 — 2026-06-21 — getMemory(key, { soft }) — optional ?soft=1 read returning 200/null
7
7
  * for keys that may not exist yet (kills browser-console 404 noise).
8
+ * v1.3.0 — 2026-06-22 — Scalable Memory tab helpers: listMemoriesMeta (include=meta, values omitted),
9
+ * searchMemory prefix arg, exportMemory/importMemory/bulkDeleteMemory.
8
10
  */
9
11
  import { api, apiGet, apiPost, apiDelete } from '/js/api.js';
10
12
 
@@ -16,6 +18,19 @@ export async function listMemories(agentGaii) {
16
18
  return Array.isArray(list) ? list : [];
17
19
  }
18
20
 
21
+ /**
22
+ * List memory metadata only — keys/tags/visibility/timestamps + per-entry `bytes`, NO values
23
+ * (include=meta). Fast for thousands of keys; values are fetched per-key on expand via getMemory().
24
+ * Returns { items, quota } so the tab can render the storage-usage bar.
25
+ */
26
+ export async function listMemoriesMeta(agentGaii) {
27
+ let url = '/v1/memory?include=meta';
28
+ if (agentGaii) url += `&agent=${encodeURIComponent(agentGaii)}`;
29
+ const data = await apiGet(url);
30
+ const list = data?.data?.items || data?.data?.entries || [];
31
+ return { items: Array.isArray(list) ? list : [], quota: data?.data?.quota || null };
32
+ }
33
+
19
34
  /** Cheap count of owner-scope memory entries (no values transferred). For stat displays. */
20
35
  export async function countMemories() {
21
36
  const data = await apiGet('/v1/memory?count=true');
@@ -29,19 +44,62 @@ export async function countMemories() {
29
44
  * instead of a 404, avoiding browser-console 404 noise.
30
45
  */
31
46
  export async function getMemory(key, opts) {
32
- const q = opts && opts.soft ? '?soft=1' : '';
33
- return apiGet(`/v1/memory/${encodeURIComponent(key)}${q}`);
47
+ const params = new URLSearchParams();
48
+ if (opts && opts.soft) params.set('soft', '1');
49
+ if (opts && opts.agent) params.set('agent', opts.agent);
50
+ const q = params.toString();
51
+ return apiGet(`/v1/memory/${encodeURIComponent(key)}${q ? '?' + q : ''}`);
34
52
  }
35
53
 
36
- /** Search memory by query string. Returns array. Optional agentGaii. */
37
- export async function searchMemory(query, agentGaii) {
54
+ /**
55
+ * Search memory by query string (server-side: matches key, value, and tags). Returns array.
56
+ * @param {string} [agentGaii] Owner-session: restrict to one agent's keyspace.
57
+ * @param {string} [prefix] Scope the search to a namespace/group (keys beginning with prefix).
58
+ */
59
+ export async function searchMemory(query, agentGaii, prefix) {
38
60
  let url = `/v1/memory/search?q=${encodeURIComponent(query)}`;
39
61
  if (agentGaii) url += `&agent=${encodeURIComponent(agentGaii)}`;
62
+ if (prefix) url += `&prefix=${encodeURIComponent(prefix)}`;
40
63
  const data = await apiGet(url);
41
64
  const list = data?.data?.results || data?.data || [];
42
65
  return Array.isArray(list) ? list : [];
43
66
  }
44
67
 
68
+ /**
69
+ * Export all of the caller's memory entries (full values) as a JSON backup object
70
+ * ({ exported_at, node_id, count, entries }). Optional agentGaii / prefix scope.
71
+ */
72
+ export async function exportMemory(agentGaii, prefix) {
73
+ const params = new URLSearchParams();
74
+ if (agentGaii) params.set('agent', agentGaii);
75
+ if (prefix) params.set('prefix', prefix);
76
+ const qs = params.toString();
77
+ const data = await apiGet('/v1/memory/export' + (qs ? '?' + qs : ''));
78
+ return data?.data || { entries: [], count: 0 };
79
+ }
80
+
81
+ /**
82
+ * Import (restore/merge) a backup's entries. mode = 'skip' | 'overwrite' | 'rename'.
83
+ * Returns the summary envelope ({ data: { created, updated, skipped, failed } }).
84
+ */
85
+ export async function importMemory(entries, mode, agentGaii) {
86
+ const body = { entries, mode: mode || 'skip' };
87
+ if (agentGaii) body.agent = agentGaii;
88
+ return apiPost('/v1/memory/import', body);
89
+ }
90
+
91
+ /**
92
+ * Bulk-delete memory entries by prefix and/or an explicit key list. Returns envelope ({ data: { deleted } }).
93
+ * @param {{ prefix?: string, keys?: string[], agentGaii?: string }} [opts]
94
+ */
95
+ export async function bulkDeleteMemory({ prefix, keys, agentGaii } = {}) {
96
+ const body = {};
97
+ if (prefix) body.prefix = prefix;
98
+ if (keys && keys.length) body.keys = keys;
99
+ if (agentGaii) body.agent = agentGaii;
100
+ return apiPost('/v1/memory/bulk-delete', body);
101
+ }
102
+
45
103
  /**
46
104
  * Librarian full-text search across all of the caller's content (GET /v1/librarian/search).
47
105
  * Returns the ranked hits array (each: { key, title, snippet, score, organismId, workspaceId, ... }).
@@ -6,6 +6,8 @@
6
6
  * resolve gate approvals.
7
7
  * @usage import * as orgService from '/js/services/organisms.js';
8
8
  * @version-history
9
+ * v1.3.0 — 2026-06-22 — Batch endpoints: discoverWorkspaces({include:'enrichment'}) folds the
10
+ * per-workspace fan-out; new getWaiting() (home widget) and listCommentsBatch() (workspace comments).
9
11
  * v1.2.1 — 2026-06-21 — Decode HTML entities in workspace names on read (listWorkspaces +
10
12
  * discoverWorkspaces) so legacy double-escaped registry names (e.g. "STT & Voice") render
11
13
  * as plain text in the list + breadcrumb.
@@ -38,6 +40,7 @@ export async function listOrganisms(opts = {}) {
38
40
  if (opts.interest) params.set('interest', opts.interest);
39
41
  if (opts.visibility) params.set('visibility', opts.visibility);
40
42
  if (opts.member) params.set('member', opts.member);
43
+ if (opts.include) params.set('include', opts.include); // 'counts' → each org gains workspace_count
41
44
  return apiGet(`/v1/organisms?${params.toString()}`);
42
45
  }
43
46
 
@@ -90,12 +93,43 @@ export async function reviewJoinRequest(organismId, requestId, decision) {
90
93
  return apiPost(`/v1/organisms/${encodeURIComponent(organismId)}/join-requests/${encodeURIComponent(requestId)}/review`, { decision });
91
94
  }
92
95
 
96
+ /** "Waiting for you" — pending reviews + join-requests + invitations aggregated across the caller's
97
+ * member organisms in ONE request (replaces the home widget's per-org listApprovals/listJoinRequests/
98
+ * listWorkspaces fan-out). Returns the flat `items` array ({kind:'review'|'join'|'invite', …}). */
99
+ export async function getWaiting() {
100
+ try {
101
+ const resp = await apiGet('/v1/organisms/waiting');
102
+ return Array.isArray(resp?.data?.items) ? resp.data.items : [];
103
+ } catch { return []; }
104
+ }
105
+
106
+ /** Agent activity aggregated across every readable workspace in ONE request (organism Agents tab).
107
+ * Returns { agentName: { count, lastAt, workspaces:[names] } } (replaces per-workspace activity fetches). */
108
+ export async function getAgentsActivity(orgId) {
109
+ try {
110
+ const resp = await apiGet(`/v1/organisms/${encodeURIComponent(orgId)}/agents/activity`);
111
+ return resp?.data?.agents || {};
112
+ } catch { return {}; }
113
+ }
114
+
93
115
  /** List the comment thread on a workspace object (record or document). */
94
116
  export async function listComments(orgId, ws, space, instanceId) {
95
117
  const params = new URLSearchParams({ ws, space, instance_id: instanceId });
96
118
  return apiGet(`/v1/organisms/${encodeURIComponent(orgId)}/comments?${params.toString()}`);
97
119
  }
98
120
 
121
+ /** Composite key the comments-batch response is keyed by (NUL-joined, never collides with any id). */
122
+ export const commentBatchKey = (ws, space, instanceId) => `${ws}\u0000${space}\u0000${instanceId}`;
123
+
124
+ /** Fetch comment threads for MANY workspace objects in one POST (body, not URL params, so a long
125
+ * target list never bloats the URL). Returns the raw `{ "ws\0space\0id": {comments,total} }` map. */
126
+ export async function listCommentsBatch(orgId, instances) {
127
+ try {
128
+ const resp = await apiPost(`/v1/organisms/${encodeURIComponent(orgId)}/comments/batch`, { instances });
129
+ return (resp?.data?.comments) || {};
130
+ } catch { return {}; }
131
+ }
132
+
99
133
  /** Add a comment to a workspace object. anchor: { section?|quote? }; parentId for a threaded reply. */
100
134
  export async function addComment(orgId, { ws, space, instanceId, body, anchor, parentId }) {
101
135
  return apiPost(`/v1/organisms/${encodeURIComponent(orgId)}/comments`, { ws, space, instance_id: instanceId, body, anchor, parent_id: parentId });
@@ -579,10 +613,13 @@ export async function saveWorkspaceRegistry(orgId, workspaces) {
579
613
  * Organism membership lets you DISCOVER every workspace (names + creator + your access status);
580
614
  * reading a workspace's content needs the creator's approval. ── */
581
615
 
582
- /** Discover all workspaces in the org with your access status ('owner' | 'granted' | 'none'). */
583
- export async function discoverWorkspaces(orgId) {
616
+ /** Discover all workspaces in the org with your access status ('owner' | 'granted' | 'none').
617
+ * opts.include='enrichment' folds the per-workspace getWorkspace+activity+participants fan-out into
618
+ * one response: each readable row gains `enrichment:{hasManifest,recs,docs,lastEvent,participants,pendingReviews}`. */
619
+ export async function discoverWorkspaces(orgId, opts = {}) {
584
620
  try {
585
- const resp = await apiGet(`/v1/organisms/${encodeURIComponent(orgId)}/workspaces`);
621
+ const qs = opts.include ? `?include=${encodeURIComponent(opts.include)}` : '';
622
+ const resp = await apiGet(`/v1/organisms/${encodeURIComponent(orgId)}/workspaces${qs}`);
586
623
  const list = Array.isArray(resp?.data?.workspaces) ? resp.data.workspaces : [];
587
624
  // Names are plain text; legacy import paths stored them HTML-escaped. Decode for display.
588
625
  return list.map(w => (w && w.name ? { ...w, name: decodeEntities(w.name) } : w));
@@ -615,6 +652,15 @@ export async function getWorkspaceAccess(orgId, ws) {
615
652
  } catch { return { requests: [], members: [] }; }
616
653
  }
617
654
 
655
+ /** Access rosters for ALL of my owned workspaces in one request (Members tab — replaces a per-owned-
656
+ * workspace getWorkspaceAccess fan-out). Returns [{ ws, name, members, requests }]. */
657
+ export async function getWorkspaceAccessAll(orgId) {
658
+ try {
659
+ const resp = await apiGet(`/v1/organisms/${encodeURIComponent(orgId)}/workspace-access?all=1`);
660
+ return Array.isArray(resp?.data?.workspaces) ? resp.data.workspaces : [];
661
+ } catch { return []; }
662
+ }
663
+
618
664
  /** Directly add (or re-role) a member: role 'viewer' (read) | 'contributor' (read+write). grantee = an
619
665
  * owner name, GHII, or GAII — the grant applies to that owner (so all their agents inherit it). */
620
666
  export async function grantWorkspaceRole(orgId, ws, grantee, role) {
@@ -23,9 +23,10 @@ export async function getAgentOffers(agentName) {
23
23
  return apiGet(`/v1/agents/${encodeURIComponent(agentName)}/offers`);
24
24
  }
25
25
 
26
- /** List the owner's workflow definitions: { workflows, count }. */
27
- export async function listWorkflows() {
28
- return apiGet('/v1/workflows');
26
+ /** List the owner's workflow definitions: { workflows, count }. opts.include='health' attaches each
27
+ * workflow's run-health inline (avoids a per-workflow getHealth fan-out in the list view). */
28
+ export async function listWorkflows(opts = {}) {
29
+ return apiGet(`/v1/workflows${opts.include ? `?include=${encodeURIComponent(opts.include)}` : ''}`);
29
30
  }
30
31
 
31
32
  export async function getWorkflow(id) {
@@ -240,6 +240,7 @@
240
240
  "/views/profile/organisms/sources-panel.js": "/views/profile/organisms/sources-panel.js",
241
241
  "/views/profile/organisms/schema-form.js": "/views/profile/organisms/schema-form.js",
242
242
  "/views/profile/organisms/document.js": "/views/profile/organisms/document.js",
243
+ "/views/command-palette.js": "/views/command-palette.js",
243
244
  "/views/profile/organisms/mindmap.js": "/views/profile/organisms/mindmap.js",
244
245
  "/views/profile/organisms/readme-panel.js": "/views/profile/organisms/readme-panel.js",
245
246
  "/views/profile/organisms/timeline-panel.js": "/views/profile/organisms/timeline-panel.js"
@@ -286,6 +287,7 @@
286
287
  import { loadTranslations, t, getLocale, switchLocale, onLocaleChange } from '/js/i18n.js';
287
288
  import { initThemeSystem, getTheme, toggleTheme, onThemeChange } from '/js/theme.js';
288
289
  import { NotificationBell } from '/components/NotificationBell.js';
290
+ import { CommandPalette } from '/views/command-palette.js';
289
291
  import { openAppSandboxed, isAppHtmlUrl } from '/js/app-sandbox.js';
290
292
  // generateStars no longer needed for light theme
291
293
 
@@ -467,6 +469,7 @@
467
469
 
468
470
  return html`
469
471
  <${Header} navigate=${navigate} locale=${locale} />
472
+ <${CommandPalette} navigate=${navigate} />
470
473
  <div class="page-content">
471
474
  ${viewError
472
475
  ? html`<div style="text-align:center; padding:3rem 1.5rem; color:var(--text-dim,#888);">