mulmoclaude 0.9.2 → 0.9.4

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 (67) hide show
  1. package/bin/mulmoclaude.js +2 -1
  2. package/client/assets/{index-Dc0R-HW5.js → index-B3NxFcEH.js} +46 -46
  3. package/client/assets/{index-Dxo1Zdd-.css → index-BXb--as6.css} +1 -1
  4. package/client/assets/{marp-CSq0PPfK.js → marp-C9QDHFAJ.js} +1 -1
  5. package/client/index.html +2 -2
  6. package/package.json +11 -10
  7. package/server/api/routes/agent.ts +5 -1
  8. package/server/api/routes/chat-index.ts +5 -1
  9. package/server/api/routes/collections.ts +152 -1
  10. package/server/api/routes/config.ts +12 -0
  11. package/server/index.ts +14 -2
  12. package/server/prompts/system/system.md +7 -1
  13. package/server/remoteHost/firebase.ts +6 -0
  14. package/server/remoteHost/handlers/collectionPage.ts +15 -17
  15. package/server/remoteHost/handlers/getCollection.ts +2 -2
  16. package/server/remoteHost/handlers/getFeed.ts +2 -2
  17. package/server/remoteHost/handlers/getRemoteView.ts +37 -0
  18. package/server/remoteHost/handlers/getRemoteViewItems.ts +38 -0
  19. package/server/remoteHost/handlers/index.ts +10 -0
  20. package/server/remoteHost/handlers/ingestAttachments.ts +88 -0
  21. package/server/remoteHost/handlers/listAccountingBooks.ts +31 -0
  22. package/server/remoteHost/handlers/listCollections.ts +6 -1
  23. package/server/remoteHost/handlers/listSkills.ts +39 -0
  24. package/server/remoteHost/handlers/mutateRemoteView.ts +40 -0
  25. package/server/remoteHost/handlers/startChat.ts +103 -39
  26. package/server/system/config.ts +101 -4
  27. package/server/system/logs/aaa +737 -0
  28. package/server/system/logs/bb +446 -0
  29. package/server/utils/files/thumbnail-store.ts +97 -0
  30. package/server/workspace/chat-index/index.ts +29 -3
  31. package/server/workspace/chat-index/indexer.ts +187 -29
  32. package/server/workspace/chat-index/summarizer.ts +18 -10
  33. package/server/workspace/collections/index.ts +2 -1
  34. package/server/workspace/collections/remoteView.ts +290 -0
  35. package/server/workspace/journal/archivist-cli.ts +17 -3
  36. package/server/workspace/journal/dailyPass.ts +52 -2
  37. package/server/workspace/journal/index.ts +20 -4
  38. package/src/App.vue +62 -3
  39. package/src/components/FileTree.vue +21 -1
  40. package/src/components/FileTreePane.vue +41 -25
  41. package/src/components/FilesView.vue +4 -0
  42. package/src/components/RemoteHostControl.vue +18 -0
  43. package/src/components/SessionHistoryPanel.vue +24 -11
  44. package/src/components/SettingsChatIndexTab.vue +119 -0
  45. package/src/components/SettingsJournalTab.vue +117 -0
  46. package/src/components/SettingsModal.vue +12 -2
  47. package/src/composables/collections/uiHost.ts +15 -0
  48. package/src/composables/collections/useDynamicShortcutIcons.ts +93 -0
  49. package/src/composables/usePubSub.ts +40 -2
  50. package/src/composables/useSessionSync.ts +9 -0
  51. package/src/composables/useShowHiddenSystemFiles.ts +23 -0
  52. package/src/config/apiRoutes.ts +20 -0
  53. package/src/config/roles.ts +2 -2
  54. package/src/config/visibleWorkspaceDirs.ts +21 -0
  55. package/src/lang/de.ts +47 -0
  56. package/src/lang/en.ts +45 -0
  57. package/src/lang/es.ts +46 -0
  58. package/src/lang/fr.ts +47 -0
  59. package/src/lang/ja.ts +46 -0
  60. package/src/lang/ko.ts +45 -0
  61. package/src/lang/pt-BR.ts +46 -0
  62. package/src/lang/zh.ts +44 -0
  63. package/src/plugins/textResponse/View.vue +49 -14
  64. package/src/plugins/textResponse/utils.ts +37 -0
  65. package/src/utils/html/previewCsp.ts +7 -16
  66. package/src/utils/role/icon.ts +9 -2
  67. package/src/utils/session/sessionPreview.ts +29 -0
@@ -7,11 +7,17 @@ interface PubSubMessage {
7
7
 
8
8
  type Callback = (data: unknown) => void;
9
9
  type Unsubscribe = () => void;
10
+ type ReconnectHandler = () => void;
10
11
 
11
12
  // On reconnect we re-emit every live subscription so the rooms list survives the bounce.
12
13
  let socket: Socket | null = null;
13
14
 
14
15
  const listeners = new Map<string, Set<Callback>>();
16
+ const reconnectHandlers = new Set<ReconnectHandler>();
17
+ // First `connect` is the initial establishment; every subsequent one is a
18
+ // reconnect and needs a state catch-up (missed events during the down window
19
+ // are lost because the pub/sub server has no replay buffer). See #1915.
20
+ let hasConnectedOnce = false;
15
21
 
16
22
  function resendSubscriptions(sock: Socket): void {
17
23
  for (const channel of listeners.keys()) {
@@ -19,6 +25,16 @@ function resendSubscriptions(sock: Socket): void {
19
25
  }
20
26
  }
21
27
 
28
+ function fireReconnectHandlers(): void {
29
+ for (const handler of reconnectHandlers) {
30
+ try {
31
+ handler();
32
+ } catch (err) {
33
+ console.error("[usePubSub] reconnect handler threw:", err);
34
+ }
35
+ }
36
+ }
37
+
22
38
  function connect(): Socket {
23
39
  if (socket) return socket;
24
40
 
@@ -28,7 +44,14 @@ function connect(): Socket {
28
44
  transports: ["websocket"],
29
45
  });
30
46
 
31
- sock.on("connect", () => resendSubscriptions(sock));
47
+ sock.on("connect", () => {
48
+ resendSubscriptions(sock);
49
+ if (hasConnectedOnce) {
50
+ fireReconnectHandlers();
51
+ } else {
52
+ hasConnectedOnce = true;
53
+ }
54
+ });
32
55
 
33
56
  sock.on("data", (msg: PubSubMessage) => {
34
57
  const cbs = listeners.get(msg.channel);
@@ -46,6 +69,10 @@ function maybeDisconnect(): void {
46
69
  if (!socket) return;
47
70
  socket.disconnect();
48
71
  socket = null;
72
+ // Reset so the next `connect()` cycle is a true initial connect again —
73
+ // catch-up handlers only make sense against state accumulated on this
74
+ // socket, not across an unsubscribe/resubscribe cycle.
75
+ hasConnectedOnce = false;
49
76
  }
50
77
 
51
78
  export function usePubSub() {
@@ -73,5 +100,16 @@ export function usePubSub() {
73
100
  };
74
101
  }
75
102
 
76
- return { subscribe };
103
+ // Register a handler that fires on every reconnect (not the initial
104
+ // connect). Callers use this to catch up on missed events after the
105
+ // socket bounces — the pub/sub server has no replay buffer, so anything
106
+ // published during the down window is gone. See #1915.
107
+ function onReconnect(handler: ReconnectHandler): Unsubscribe {
108
+ reconnectHandlers.add(handler);
109
+ return () => {
110
+ reconnectHandlers.delete(handler);
111
+ };
112
+ }
113
+
114
+ return { subscribe, onReconnect };
77
115
  }
@@ -30,7 +30,15 @@ export function useSessionSync(opts: {
30
30
  const { sessionMap, currentSessionId, fetchSessions, onCurrentSessionDeleted } = opts;
31
31
  const { subscribe } = usePubSub();
32
32
 
33
+ // Monotonic sequence token — protects sessionMap from stale overwrites when
34
+ // two concurrent refreshes race (e.g. reconnect fires while a
35
+ // visibilitychange is mid-flight). Every call increments the token before
36
+ // awaiting; after the fetch resolves, we mutate only if our token is still
37
+ // the latest, so the older-but-slower response can never regress live
38
+ // state (e.g. re-flip isRunning back to true after session_finished).
39
+ let refreshToken = 0;
33
40
  async function refreshSessionStates(): Promise<void> {
41
+ const myToken = ++refreshToken;
34
42
  let summaries: SessionSummary[];
35
43
  try {
36
44
  summaries = await fetchSessions();
@@ -40,6 +48,7 @@ export function useSessionSync(opts: {
40
48
  console.warn("[session-sync] failed to fetch sessions:", err);
41
49
  return;
42
50
  }
51
+ if (myToken !== refreshToken) return;
43
52
  for (const summary of summaries) {
44
53
  const live = sessionMap.get(summary.id);
45
54
  if (!live) continue;
@@ -0,0 +1,23 @@
1
+ // Composable: "show system files" toggle for the Files Explorer,
2
+ // persisted to localStorage. Default false — the Files pane hides
3
+ // agent-internal top-level dirs (`conversations/`, `feeds/`, etc.)
4
+ // until the user opts in. See #1896.
5
+
6
+ import { ref } from "vue";
7
+
8
+ const STORAGE_KEY = "filesView.showHiddenSystem";
9
+
10
+ function readStored(): boolean {
11
+ return localStorage.getItem(STORAGE_KEY) === "true";
12
+ }
13
+
14
+ export function useShowHiddenSystemFiles() {
15
+ const showHiddenSystem = ref<boolean>(readStored());
16
+
17
+ function setShowHiddenSystem(next: boolean): void {
18
+ showHiddenSystem.value = next;
19
+ localStorage.setItem(STORAGE_KEY, next ? "true" : "false");
20
+ }
21
+
22
+ return { showHiddenSystem, setShowHiddenSystem };
23
+ }
@@ -293,6 +293,26 @@ const HOST_API_ROUTES = {
293
293
  /** GET ?id=<viewId> → the custom view's HTML file (global-bearer auth),
294
294
  * read from data/skills/:slug/views/. The parent renders it sandboxed. */
295
295
  viewFile: "/api/collections/:slug/view-file",
296
+ /** GET ?id=<viewId>&locale=<tag> → a mobile (`target: "mobile"`) custom
297
+ * view wrapped into its sandboxed srcdoc (global-bearer auth) →
298
+ * { view, srcdoc, bytes }. Same builder as the command channel's
299
+ * `getRemoteView`, so the desktop phone-frame preview renders the exact
300
+ * artifact the phone receives (plans/feat-remote-custom-view.md). */
301
+ remoteView: "/api/collections/:slug/remote-view",
302
+ /** POST { op: "update"|"delete", id, patch? } → apply one mutate on behalf
303
+ * of a `target: "mobile"` view, authorized by that view's declared
304
+ * editableFields / allowDelete and enforced host-side (global-bearer auth).
305
+ * The desktop phone-frame preview's write channel — same builder the
306
+ * command channel's `mutateRemoteViewItem` uses, so preview === phone
307
+ * (plans/feat-remote-writable-view.md). */
308
+ remoteViewMutate: "/api/collections/:slug/remote-view/:viewId/mutate",
309
+ /** GET ?offset&limit&fields=<csv> → one page of a `target: "mobile"` view's
310
+ * records with its declared `imageFields` inlined as `data:` URL thumbnails
311
+ * (global-bearer auth) → { page, inlined, omitted }. Same builder as the
312
+ * command channel's `getRemoteViewItems`, so the desktop phone-frame preview
313
+ * pages the exact data (incl. real thumbnails) the phone will
314
+ * (plans/feat-remote-view-images.md). */
315
+ remoteViewItems: "/api/collections/:slug/remote-view/:viewId/items",
296
316
  /** GET ?id=<viewId>&locale=<tag> → translation dict for one custom view
297
317
  * (global-bearer auth) → { locale, dict }. `dict` is the host-picked
298
318
  * flat map for the requested locale (fallback `"en"`, else `{}`); the
@@ -46,7 +46,7 @@ export const ROLES: Role[] = [
46
46
  {
47
47
  id: "general",
48
48
  name: "General",
49
- icon: "star",
49
+ icon: "auto_awesome",
50
50
  prompt:
51
51
  "You are a helpful assistant with access to the user's workspace. Help with tasks, answer questions, and use available tools when appropriate.\n\n" +
52
52
  "## Asking the user to choose\n\n" +
@@ -372,7 +372,7 @@ export const ROLES: Role[] = [
372
372
  {
373
373
  id: "debug",
374
374
  name: "Debug",
375
- icon: "star",
375
+ icon: "bug_report",
376
376
  prompt:
377
377
  "You are a helpful assistant with access to the user's workspace. Help with tasks, answer questions, and use available tools when appropriate.\n\n" +
378
378
  "## Asking the user to choose\n\n" +
@@ -0,0 +1,21 @@
1
+ // Top-level workspace directories that the Files Explorer surfaces by
2
+ // default. Everything else at the root (`conversations/`, `feeds/`,
3
+ // `.git/`, `.github/`, ad-hoc automation buckets, …) is treated as
4
+ // "system" and stays hidden until the user flips the "show system
5
+ // files" toggle. See #1896.
6
+ //
7
+ // The whitelist covers the buckets MulmoClaude WRITES USER-FACING
8
+ // CONTENT into:
9
+ // - data/ — wiki, calendar, contacts, attachments, feed records, ...
10
+ // - artifacts/ — charts, documents, html, svg, images, spreadsheets, ...
11
+ // - config/ — settings.json, mcp.json, roles/, helps/, marp themes
12
+ // The filter is applied only at the root — once a whitelisted dir is
13
+ // shown, everything beneath it is visible without further filtering.
14
+ // That way a plugin that lands `data/foo-plugin/` shows up naturally
15
+ // without needing a code change here.
16
+
17
+ export const VISIBLE_TOP_LEVEL_DIRS: readonly string[] = ["data", "artifacts", "config"];
18
+
19
+ export function isVisibleTopLevel(name: string): boolean {
20
+ return VISIBLE_TOP_LEVEL_DIRS.includes(name);
21
+ }
package/src/lang/de.ts CHANGED
@@ -136,6 +136,9 @@ const deMessages = {
136
136
  disconnectFailed: "Trennen fehlgeschlagen",
137
137
  signInFailed: "Google-Anmeldung fehlgeschlagen",
138
138
  statusFailed: "Status konnte nicht geladen werden",
139
+ description: "Mit Remote-Zugriff kann ein Mobilgerät auf die Sammlungen und Feeds dieser MulmoClaude-Instanz zugreifen.",
140
+ howTo: "Öffne auf deinem Smartphone {url} und melde dich mit demselben Google-Konto an.",
141
+ customViewHint: "Für eine mobiltaugliche Ansicht bitte Claude, statt einer normalen Custom View eine {keyword} zu bauen.",
139
142
  },
140
143
  sidebarHeader: {
141
144
  home: "Zum neuesten Chat",
@@ -180,6 +183,9 @@ const deMessages = {
180
183
  // "RO" = Read-Only. Englische Abkürzung bleibt erhalten, um als
181
184
  // kompaktes Badge neben dem Label "Referenz" zu passen.
182
185
  readOnlyBadge: "RO",
186
+ showSystemFiles: "Systemdateien anzeigen",
187
+ showSystemFilesTitle:
188
+ "Zeigt agent-interne Top-Level-Verzeichnisse (conversations/, feeds/ usw.) zusätzlich zu den Nutzer-Daten (data/, artifacts/, config/) an.",
183
189
  },
184
190
  fileTree: {
185
191
  workspace: "(Arbeitsbereich)",
@@ -231,6 +237,8 @@ const deMessages = {
231
237
  photos: "Fotos",
232
238
  model: "Modell",
233
239
  voice: "Sprache",
240
+ chatIndex: "Chat-Index",
241
+ journal: "Journal",
234
242
  skills: "Skills",
235
243
  roles: "Rollen",
236
244
  },
@@ -293,6 +301,43 @@ const deMessages = {
293
301
  loadError: "Einstellungen konnten nicht geladen werden",
294
302
  saveError: "Speichern fehlgeschlagen",
295
303
  },
304
+ chatIndexTab: {
305
+ description:
306
+ "Automatische KI-Titel und Zusammenfassungen für den Chat-Verlauf. Standardmäßig aus — Automatisierungs-Sessions (Scheduler / System-Worker) werden auch bei aktivierter Option immer übersprungen; menschliche Sessions kosten pro beendetem Turn genau einen Summarizer-Aufruf.",
307
+ modeLabel: "Chat-Index-Modell",
308
+ helperText: "Haiku ist günstiger; Sonnet liefert schärfere Titel bei langen, themenwechselnden Sessions.",
309
+ mode: {
310
+ off: "Aus",
311
+ haiku: "Haiku",
312
+ sonnet: "Sonnet",
313
+ },
314
+ status: {
315
+ off: "Indizierung ist AUS",
316
+ haiku: "Indizierung läuft mit Haiku",
317
+ sonnet: "Indizierung läuft mit Sonnet",
318
+ },
319
+ loadError: "Einstellungen konnten nicht geladen werden",
320
+ saveError: "Speichern fehlgeschlagen",
321
+ },
322
+ journalTab: {
323
+ description:
324
+ "Automatisiertes Tages-Journal — fasst kürzliche Chat-Sessions in journal/*.md zusammen und extrahiert dauerhafte Memory-Notizen. Standardmäßig aus. Automatisierungs-Sessions (Scheduler / System-Worker) werden unabhängig von dieser Einstellung immer ausgeschlossen.",
325
+ modeLabel: "Journal-Modell",
326
+ helperText:
327
+ "Haiku ist günstiger; Sonnet liefert reichhaltigere Tages- und Themen-Zusammenfassungen. Der stündliche Lauf startet nur, wenn dies gesetzt ist.",
328
+ mode: {
329
+ off: "Aus",
330
+ haiku: "Haiku",
331
+ sonnet: "Sonnet",
332
+ },
333
+ status: {
334
+ off: "Journal ist AUS",
335
+ haiku: "Journal läuft mit Haiku",
336
+ sonnet: "Journal läuft mit Sonnet",
337
+ },
338
+ loadError: "Einstellungen konnten nicht geladen werden",
339
+ saveError: "Speichern fehlgeschlagen",
340
+ },
296
341
  // `<i18n-t>`-Slots — die Namen `envKey` / `envFile` werden in
297
342
  // SettingsModal.vue als Inline-`<code>` gerendert, sodass die
298
343
  // literalen Variablen- und Dateinamen unübersetzt bleiben.
@@ -949,6 +994,8 @@ const deMessages = {
949
994
  cancel: "Abbrechen",
950
995
  seededByPlugin: "von {pkg}",
951
996
  seededByPluginTooltip: "Diese Nachricht wurde vom Plugin {pkg} erstellt und nicht von Ihnen gesendet.",
997
+ truncatedForRender:
998
+ "Diese Nachricht ist ungewöhnlich lang (insgesamt {total} Zeichen). Nur der erste Teil wird angezeigt – {omitted} Zeichen ausgeblendet, damit der Tab reaktionsfähig bleibt. Nutze „Kopieren“ für den vollständigen Text.",
952
999
  },
953
1000
  pluginSkill: {
954
1001
  noDescription: "(keine Beschreibung)",
package/src/lang/en.ts CHANGED
@@ -154,6 +154,9 @@ const enMessages = {
154
154
  disconnectFailed: "Disconnect failed",
155
155
  signInFailed: "Google sign-in failed",
156
156
  statusFailed: "Failed to load status",
157
+ description: "Remote access lets a mobile device connect to this MulmoClaude's collections and feeds.",
158
+ howTo: "On your phone, open {url} and sign in with the same Google account.",
159
+ customViewHint: "For a mobile-friendly view, ask Claude to build a {keyword} (not a regular custom view).",
157
160
  },
158
161
  sidebarHeader: {
159
162
  home: "Go to latest chat",
@@ -198,6 +201,8 @@ const enMessages = {
198
201
  // "RO" = Read-Only. Kept short on purpose — rendered as a compact
199
202
  // badge next to the Reference label.
200
203
  readOnlyBadge: "RO",
204
+ showSystemFiles: "Show system files",
205
+ showSystemFilesTitle: "Show agent-internal top-level dirs (conversations/, feeds/, etc.) in addition to your user content (data/, artifacts/, config/).",
201
206
  },
202
207
  fileTree: {
203
208
  workspace: "(workspace)",
@@ -249,6 +254,8 @@ const enMessages = {
249
254
  photos: "Photos",
250
255
  model: "Model",
251
256
  voice: "Voice",
257
+ chatIndex: "Chat index",
258
+ journal: "Journal",
252
259
  skills: "Skills",
253
260
  roles: "Roles",
254
261
  },
@@ -309,6 +316,42 @@ const enMessages = {
309
316
  loadError: "Failed to load settings",
310
317
  saveError: "Failed to save",
311
318
  },
319
+ chatIndexTab: {
320
+ description:
321
+ "Background AI titles + summaries for your chat history. Off by default — automation sessions (scheduler / system workers) are always skipped even when on, and human sessions only pay one summarizer call each when a turn ends.",
322
+ modeLabel: "Chat index model",
323
+ helperText: "Haiku is cheaper; Sonnet gives sharper titles for long, topic-shifting sessions.",
324
+ mode: {
325
+ off: "Off",
326
+ haiku: "Haiku",
327
+ sonnet: "Sonnet",
328
+ },
329
+ status: {
330
+ off: "Indexing is OFF",
331
+ haiku: "Indexing with Haiku",
332
+ sonnet: "Indexing with Sonnet",
333
+ },
334
+ loadError: "Failed to load settings",
335
+ saveError: "Failed to save",
336
+ },
337
+ journalTab: {
338
+ description:
339
+ "Automated daily journal — summarises recent chat sessions into journal/*.md and extracts durable memory notes. Off by default. Automation sessions (scheduler / system workers) are always excluded regardless of this setting.",
340
+ modeLabel: "Journal model",
341
+ helperText: "Haiku is cheaper; Sonnet produces richer daily / topic summaries. The hourly pass runs only when this is set.",
342
+ mode: {
343
+ off: "Off",
344
+ haiku: "Haiku",
345
+ sonnet: "Sonnet",
346
+ },
347
+ status: {
348
+ off: "Journal is OFF",
349
+ haiku: "Journal running with Haiku",
350
+ sonnet: "Journal running with Sonnet",
351
+ },
352
+ loadError: "Failed to load settings",
353
+ saveError: "Failed to save",
354
+ },
312
355
  // `<i18n-t>` slots — named `envKey` / `envFile` render as inline
313
356
  // `<code>` in SettingsModal.vue, so the literal variable and file
314
357
  // names stay untranslated while the surrounding copy is localised.
@@ -961,6 +1004,8 @@ const enMessages = {
961
1004
  cancel: "Cancel",
962
1005
  seededByPlugin: "from {pkg}",
963
1006
  seededByPluginTooltip: "This message was seeded by the {pkg} plugin, not sent by you.",
1007
+ truncatedForRender:
1008
+ "This message is unusually long ({total} chars total). Only the first portion is rendered — {omitted} chars hidden to keep the tab responsive. Use Copy for the full raw text.",
964
1009
  },
965
1010
  pluginSkill: {
966
1011
  noDescription: "(no description)",
package/src/lang/es.ts CHANGED
@@ -139,6 +139,9 @@ const esMessages = {
139
139
  disconnectFailed: "Error al desconectar",
140
140
  signInFailed: "Error al iniciar sesión con Google",
141
141
  statusFailed: "Error al cargar el estado",
142
+ description: "El acceso remoto permite que un dispositivo móvil se conecte a las colecciones y feeds de este MulmoClaude.",
143
+ howTo: "En tu teléfono, abre {url} e inicia sesión con la misma cuenta de Google.",
144
+ customViewHint: "Para una vista compatible con móviles, pide a Claude que cree una {keyword} (no una custom view normal).",
142
145
  },
143
146
  sidebarHeader: {
144
147
  home: "Ir al chat más reciente",
@@ -183,6 +186,9 @@ const esMessages = {
183
186
  // "RO" = Read-Only. Se mantiene la abreviatura en inglés para
184
187
  // renderizarse como una insignia compacta junto al label Referencia.
185
188
  readOnlyBadge: "RO",
189
+ showSystemFiles: "Mostrar archivos del sistema",
190
+ showSystemFilesTitle:
191
+ "Muestra los directorios raíz internos del agente (conversations/, feeds/, etc.) además del contenido del usuario (data/, artifacts/, config/).",
186
192
  },
187
193
  fileTree: {
188
194
  workspace: "(área de trabajo)",
@@ -234,6 +240,8 @@ const esMessages = {
234
240
  photos: "Fotos",
235
241
  model: "Modelo",
236
242
  voice: "Voz",
243
+ chatIndex: "Índice de chat",
244
+ journal: "Diario",
237
245
  skills: "Skills",
238
246
  roles: "Roles",
239
247
  },
@@ -294,6 +302,42 @@ const esMessages = {
294
302
  loadError: "Error al cargar los ajustes",
295
303
  saveError: "Error al guardar",
296
304
  },
305
+ chatIndexTab: {
306
+ description:
307
+ "Títulos y resúmenes automáticos generados por IA para tu historial de chat. Sale desactivado por defecto — las sesiones de automatización (scheduler / trabajadores del sistema) siempre se omiten aunque esté activado; las sesiones humanas solo pagan una llamada al resumidor al terminar cada turno.",
308
+ modeLabel: "Modelo del índice de chat",
309
+ helperText: "Haiku es más económico; Sonnet ofrece títulos más precisos en sesiones largas con cambios de tema.",
310
+ mode: {
311
+ off: "Desactivado",
312
+ haiku: "Haiku",
313
+ sonnet: "Sonnet",
314
+ },
315
+ status: {
316
+ off: "La indexación está DESACTIVADA",
317
+ haiku: "Indexando con Haiku",
318
+ sonnet: "Indexando con Sonnet",
319
+ },
320
+ loadError: "Error al cargar los ajustes",
321
+ saveError: "Error al guardar",
322
+ },
323
+ journalTab: {
324
+ description:
325
+ "Diario automático — resume las sesiones de chat recientes en journal/*.md y extrae notas de memoria duradera. Sale desactivado. Las sesiones de automatización (scheduler / trabajadores del sistema) se excluyen siempre, independientemente de esta configuración.",
326
+ modeLabel: "Modelo del diario",
327
+ helperText: "Haiku es más económico; Sonnet produce resúmenes diarios y por tema más ricos. El pase horario solo se ejecuta cuando esto está activado.",
328
+ mode: {
329
+ off: "Desactivado",
330
+ haiku: "Haiku",
331
+ sonnet: "Sonnet",
332
+ },
333
+ status: {
334
+ off: "El diario está DESACTIVADO",
335
+ haiku: "Diario en ejecución con Haiku",
336
+ sonnet: "Diario en ejecución con Sonnet",
337
+ },
338
+ loadError: "Error al cargar los ajustes",
339
+ saveError: "Error al guardar",
340
+ },
297
341
  // Slots `<i18n-t>` — los nombres `envKey` / `envFile` se renderizan
298
342
  // como `<code>` en línea en SettingsModal.vue, por lo que los
299
343
  // literales de variable y nombre de archivo se mantienen sin
@@ -947,6 +991,8 @@ const esMessages = {
947
991
  cancel: "Cancelar",
948
992
  seededByPlugin: "desde {pkg}",
949
993
  seededByPluginTooltip: "Este mensaje fue generado por el plugin {pkg}, no enviado por ti.",
994
+ truncatedForRender:
995
+ "Este mensaje es inusualmente largo ({total} caracteres en total). Solo se muestra la primera parte — {omitted} caracteres ocultos para mantener la pestaña receptiva. Usa Copiar para obtener el texto completo.",
950
996
  },
951
997
  pluginSkill: {
952
998
  noDescription: "(sin descripción)",
package/src/lang/fr.ts CHANGED
@@ -134,6 +134,9 @@ const frMessages = {
134
134
  disconnectFailed: "Échec de la déconnexion",
135
135
  signInFailed: "Échec de la connexion Google",
136
136
  statusFailed: "Échec du chargement de l'état",
137
+ description: "L'accès distant permet à un appareil mobile de se connecter aux collections et aux flux de ce MulmoClaude.",
138
+ howTo: "Sur votre téléphone, ouvrez {url} et connectez-vous avec le même compte Google.",
139
+ customViewHint: "Pour une vue adaptée au mobile, demandez à Claude de créer une {keyword} (pas une custom view classique).",
137
140
  },
138
141
  sidebarHeader: {
139
142
  home: "Aller à la dernière conversation",
@@ -178,6 +181,9 @@ const frMessages = {
178
181
  // "RO" = Read-Only. Abréviation anglaise conservée volontairement
179
182
  // pour tenir dans un badge compact à côté du libellé Référence.
180
183
  readOnlyBadge: "RO",
184
+ showSystemFiles: "Afficher les fichiers système",
185
+ showSystemFilesTitle:
186
+ "Affiche les répertoires racine internes de l'agent (conversations/, feeds/, etc.) en plus des contenus utilisateur (data/, artifacts/, config/).",
181
187
  },
182
188
  fileTree: {
183
189
  workspace: "(espace de travail)",
@@ -229,6 +235,8 @@ const frMessages = {
229
235
  photos: "Photos",
230
236
  model: "Modèle",
231
237
  voice: "Voix",
238
+ chatIndex: "Index du chat",
239
+ journal: "Journal",
232
240
  skills: "Skills",
233
241
  roles: "Rôles",
234
242
  },
@@ -289,6 +297,43 @@ const frMessages = {
289
297
  loadError: "Échec du chargement des paramètres",
290
298
  saveError: "Échec de l'enregistrement",
291
299
  },
300
+ chatIndexTab: {
301
+ description:
302
+ "Titres et résumés générés par IA pour l'historique du chat. Désactivé par défaut — les sessions d'automatisation (scheduler / workers système) sont toujours ignorées même quand c'est activé ; les sessions humaines ne paient qu'un appel au résumeur à la fin de chaque tour.",
303
+ modeLabel: "Modèle de l'index de chat",
304
+ helperText: "Haiku est moins cher ; Sonnet donne des titres plus précis pour de longues sessions changeant de sujet.",
305
+ mode: {
306
+ off: "Désactivé",
307
+ haiku: "Haiku",
308
+ sonnet: "Sonnet",
309
+ },
310
+ status: {
311
+ off: "Indexation DÉSACTIVÉE",
312
+ haiku: "Indexation avec Haiku",
313
+ sonnet: "Indexation avec Sonnet",
314
+ },
315
+ loadError: "Échec du chargement des paramètres",
316
+ saveError: "Échec de l'enregistrement",
317
+ },
318
+ journalTab: {
319
+ description:
320
+ "Journal quotidien automatisé — résume les sessions de chat récentes dans journal/*.md et extrait des notes de mémoire persistante. Désactivé par défaut. Les sessions d'automatisation (scheduler / workers système) sont toujours exclues, quel que soit ce réglage.",
321
+ modeLabel: "Modèle du journal",
322
+ helperText:
323
+ "Haiku est moins cher ; Sonnet produit des résumés quotidiens et thématiques plus riches. Le passage horaire ne s'exécute que lorsque c'est activé.",
324
+ mode: {
325
+ off: "Désactivé",
326
+ haiku: "Haiku",
327
+ sonnet: "Sonnet",
328
+ },
329
+ status: {
330
+ off: "Journal DÉSACTIVÉ",
331
+ haiku: "Journal en cours avec Haiku",
332
+ sonnet: "Journal en cours avec Sonnet",
333
+ },
334
+ loadError: "Échec du chargement des paramètres",
335
+ saveError: "Échec de l'enregistrement",
336
+ },
292
337
  // Slots `<i18n-t>` — les noms `envKey` / `envFile` sont rendus sous
293
338
  // forme de `<code>` inline dans SettingsModal.vue ; les littéraux
294
339
  // (nom de variable et nom de fichier) restent donc non traduits.
@@ -937,6 +982,8 @@ const frMessages = {
937
982
  cancel: "Annuler",
938
983
  seededByPlugin: "depuis {pkg}",
939
984
  seededByPluginTooltip: "Ce message a été généré par le plugin {pkg}, et non envoyé par vous.",
985
+ truncatedForRender:
986
+ "Ce message est exceptionnellement long ({total} caractères au total). Seule la première partie est affichée — {omitted} caractères masqués pour garder l'onglet réactif. Utilisez « Copier » pour obtenir le texte complet.",
940
987
  },
941
988
  pluginSkill: {
942
989
  noDescription: "(aucune description)",
package/src/lang/ja.ts CHANGED
@@ -139,6 +139,9 @@ const jaMessages = {
139
139
  disconnectFailed: "切断に失敗しました",
140
140
  signInFailed: "Google サインインに失敗しました",
141
141
  statusFailed: "状態の取得に失敗しました",
142
+ description: "リモートアクセスを有効にすると、モバイルデバイスからこの MulmoClaude のコレクションとフィードに接続できます。",
143
+ howTo: "モバイルから {url} を開き、同じ Google アカウントでサインインしてください。",
144
+ customViewHint: "モバイル向けのビューが必要な場合は、通常の custom view ではなく {keyword} を作るように Claude に依頼してください。",
142
145
  },
143
146
  sidebarHeader: {
144
147
  home: "最新のチャットに移動",
@@ -181,6 +184,9 @@ const jaMessages = {
181
184
  recent: "最近",
182
185
  reference: "参照",
183
186
  readOnlyBadge: "RO",
187
+ showSystemFiles: "システムファイルを表示",
188
+ showSystemFilesTitle:
189
+ "エージェント内部の top-level ディレクトリ (conversations/ や feeds/ など) をユーザーデータ (data/ artifacts/ config/) と合わせて表示します。",
184
190
  },
185
191
  fileTree: {
186
192
  workspace: "(ワークスペース)",
@@ -232,6 +238,8 @@ const jaMessages = {
232
238
  photos: "写真",
233
239
  model: "モデル",
234
240
  voice: "音声",
241
+ chatIndex: "チャットインデックス",
242
+ journal: "ジャーナル",
235
243
  skills: "スキル",
236
244
  roles: "ロール",
237
245
  },
@@ -292,6 +300,42 @@ const jaMessages = {
292
300
  loadError: "設定の読み込みに失敗しました",
293
301
  saveError: "保存に失敗しました",
294
302
  },
303
+ chatIndexTab: {
304
+ description:
305
+ "チャット履歴の AI タイトル/サマリー自動生成の設定です。デフォルトは off で、自動化系のセッション(scheduler / system worker)は on にしても常に除外されます。人間のセッションのみターン終了時に 1 回要約が走ります。",
306
+ modeLabel: "チャットインデックスのモデル",
307
+ helperText: "Haiku は安価。Sonnet は長く話題が移るセッションでタイトル品質が高くなります。",
308
+ mode: {
309
+ off: "オフ",
310
+ haiku: "Haiku",
311
+ sonnet: "Sonnet",
312
+ },
313
+ status: {
314
+ off: "インデックス作成: オフ",
315
+ haiku: "Haiku でインデックス作成中",
316
+ sonnet: "Sonnet でインデックス作成中",
317
+ },
318
+ loadError: "設定の読み込みに失敗しました",
319
+ saveError: "保存に失敗しました",
320
+ },
321
+ journalTab: {
322
+ description:
323
+ "ジャーナル日次パスの設定です。最近のチャットセッションを journal/*.md に要約し、恒久メモリ(memory.md)を抽出します。デフォルトは off。自動化系セッション(scheduler / system worker)はこの設定に関わらず常に除外されます。",
324
+ modeLabel: "ジャーナルのモデル",
325
+ helperText: "Haiku は安価。Sonnet は日次/トピックまとめの質が高くなります。毎時のパスはここが on の時のみ実行されます。",
326
+ mode: {
327
+ off: "オフ",
328
+ haiku: "Haiku",
329
+ sonnet: "Sonnet",
330
+ },
331
+ status: {
332
+ off: "ジャーナル: オフ",
333
+ haiku: "Haiku でジャーナル実行中",
334
+ sonnet: "Sonnet でジャーナル実行中",
335
+ },
336
+ loadError: "設定の読み込みに失敗しました",
337
+ saveError: "保存に失敗しました",
338
+ },
295
339
  // `<i18n-t>` スロット — `envKey` / `envFile` は SettingsModal.vue で
296
340
  // インラインの `<code>` として描画されるため、変数名とファイル名は
297
341
  // 翻訳せずそのまま残します。
@@ -935,6 +979,8 @@ const jaMessages = {
935
979
  cancel: "キャンセル",
936
980
  seededByPlugin: "{pkg} から",
937
981
  seededByPluginTooltip: "このメッセージは {pkg} プラグインによって作成されたもので、あなたが送信したものではありません。",
982
+ truncatedForRender:
983
+ "このメッセージは非常に長いため(全 {total} 文字)、描画のフリーズを防ぐため先頭のみ表示しています({omitted} 文字を省略)。全文はコピーボタンから取得できます。",
938
984
  },
939
985
  pluginSkill: {
940
986
  noDescription: "(説明なし)",