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.
- package/bin/mulmoclaude.js +2 -1
- package/client/assets/{index-Dc0R-HW5.js → index-B3NxFcEH.js} +46 -46
- package/client/assets/{index-Dxo1Zdd-.css → index-BXb--as6.css} +1 -1
- package/client/assets/{marp-CSq0PPfK.js → marp-C9QDHFAJ.js} +1 -1
- package/client/index.html +2 -2
- package/package.json +11 -10
- package/server/api/routes/agent.ts +5 -1
- package/server/api/routes/chat-index.ts +5 -1
- package/server/api/routes/collections.ts +152 -1
- package/server/api/routes/config.ts +12 -0
- package/server/index.ts +14 -2
- package/server/prompts/system/system.md +7 -1
- package/server/remoteHost/firebase.ts +6 -0
- package/server/remoteHost/handlers/collectionPage.ts +15 -17
- package/server/remoteHost/handlers/getCollection.ts +2 -2
- package/server/remoteHost/handlers/getFeed.ts +2 -2
- package/server/remoteHost/handlers/getRemoteView.ts +37 -0
- package/server/remoteHost/handlers/getRemoteViewItems.ts +38 -0
- package/server/remoteHost/handlers/index.ts +10 -0
- package/server/remoteHost/handlers/ingestAttachments.ts +88 -0
- package/server/remoteHost/handlers/listAccountingBooks.ts +31 -0
- package/server/remoteHost/handlers/listCollections.ts +6 -1
- package/server/remoteHost/handlers/listSkills.ts +39 -0
- package/server/remoteHost/handlers/mutateRemoteView.ts +40 -0
- package/server/remoteHost/handlers/startChat.ts +103 -39
- package/server/system/config.ts +101 -4
- package/server/system/logs/aaa +737 -0
- package/server/system/logs/bb +446 -0
- package/server/utils/files/thumbnail-store.ts +97 -0
- package/server/workspace/chat-index/index.ts +29 -3
- package/server/workspace/chat-index/indexer.ts +187 -29
- package/server/workspace/chat-index/summarizer.ts +18 -10
- package/server/workspace/collections/index.ts +2 -1
- package/server/workspace/collections/remoteView.ts +290 -0
- package/server/workspace/journal/archivist-cli.ts +17 -3
- package/server/workspace/journal/dailyPass.ts +52 -2
- package/server/workspace/journal/index.ts +20 -4
- package/src/App.vue +62 -3
- package/src/components/FileTree.vue +21 -1
- package/src/components/FileTreePane.vue +41 -25
- package/src/components/FilesView.vue +4 -0
- package/src/components/RemoteHostControl.vue +18 -0
- package/src/components/SessionHistoryPanel.vue +24 -11
- package/src/components/SettingsChatIndexTab.vue +119 -0
- package/src/components/SettingsJournalTab.vue +117 -0
- package/src/components/SettingsModal.vue +12 -2
- package/src/composables/collections/uiHost.ts +15 -0
- package/src/composables/collections/useDynamicShortcutIcons.ts +93 -0
- package/src/composables/usePubSub.ts +40 -2
- package/src/composables/useSessionSync.ts +9 -0
- package/src/composables/useShowHiddenSystemFiles.ts +23 -0
- package/src/config/apiRoutes.ts +20 -0
- package/src/config/roles.ts +2 -2
- package/src/config/visibleWorkspaceDirs.ts +21 -0
- package/src/lang/de.ts +47 -0
- package/src/lang/en.ts +45 -0
- package/src/lang/es.ts +46 -0
- package/src/lang/fr.ts +47 -0
- package/src/lang/ja.ts +46 -0
- package/src/lang/ko.ts +45 -0
- package/src/lang/pt-BR.ts +46 -0
- package/src/lang/zh.ts +44 -0
- package/src/plugins/textResponse/View.vue +49 -14
- package/src/plugins/textResponse/utils.ts +37 -0
- package/src/utils/html/previewCsp.ts +7 -16
- package/src/utils/role/icon.ts +9 -2
- package/src/utils/session/sessionPreview.ts +29 -0
package/server/system/config.ts
CHANGED
|
@@ -23,6 +23,27 @@ import { isRecord, isStringArray, isStringRecord } from "../utils/types.js";
|
|
|
23
23
|
export const EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"] as const;
|
|
24
24
|
export type EffortLevel = (typeof EFFORT_LEVELS)[number];
|
|
25
25
|
|
|
26
|
+
// Chat-index summarizer setting (#1944). "off" disables the whole
|
|
27
|
+
// AI-title / summary / keywords background indexer; "haiku" / "sonnet"
|
|
28
|
+
// select the Claude model the summarizer spawns. Default (undefined)
|
|
29
|
+
// is treated as "off" — indexing is opt-in from Settings → Chat index
|
|
30
|
+
// so a fresh workspace doesn't burn CLI budget on scheduler/system
|
|
31
|
+
// automation sessions the user may never look at (see #1929 / #1944
|
|
32
|
+
// for the cost analysis). The origin filter (system + scheduler
|
|
33
|
+
// sessions always skipped) is enforced separately and does NOT
|
|
34
|
+
// depend on this setting.
|
|
35
|
+
export const CHAT_INDEX_MODES = ["off", "haiku", "sonnet"] as const;
|
|
36
|
+
export type ChatIndexMode = (typeof CHAT_INDEX_MODES)[number];
|
|
37
|
+
|
|
38
|
+
// Journal daily-pass setting (follow-up to #1944). Same three states as
|
|
39
|
+
// chat-index: "off" disables both the turn-end hook and the hourly
|
|
40
|
+
// scheduled pass; "haiku" / "sonnet" pick the Claude model the archivist
|
|
41
|
+
// CLI spawns for its summary calls. Default (undefined) is "off" so a
|
|
42
|
+
// fresh workspace doesn't burn CLI budget until the user opts in from
|
|
43
|
+
// Settings → Journal.
|
|
44
|
+
export const JOURNAL_MODES = ["off", "haiku", "sonnet"] as const;
|
|
45
|
+
export type JournalMode = (typeof JOURNAL_MODES)[number];
|
|
46
|
+
|
|
26
47
|
export interface AppSettings {
|
|
27
48
|
// Extra tool names appended to BASE_ALLOWED_TOOLS in
|
|
28
49
|
// server/agent/config.ts#buildCliArgs. Typical entries are
|
|
@@ -63,6 +84,24 @@ export interface AppSettings {
|
|
|
63
84
|
enabled: boolean;
|
|
64
85
|
model?: string;
|
|
65
86
|
};
|
|
87
|
+
|
|
88
|
+
// Chat-index summarizer mode (#1944). Ships "off" by default: the
|
|
89
|
+
// background AI-title / summary / keywords indexer stays disabled
|
|
90
|
+
// until the user opts in from Settings → Chat index. Flipping to
|
|
91
|
+
// "haiku" or "sonnet" selects the model the summarizer spawns per
|
|
92
|
+
// session. Non-user origins (`system`, `scheduler`) are always
|
|
93
|
+
// skipped regardless of this value — see `indexer.ts`.
|
|
94
|
+
chatIndex?: ChatIndexMode;
|
|
95
|
+
|
|
96
|
+
// Journal daily-pass mode (follow-up to #1944). Ships "off" by
|
|
97
|
+
// default: the archivist that summarizes chat sessions into
|
|
98
|
+
// journal/*.md stays disabled until the user opts in from Settings →
|
|
99
|
+
// Journal. Flipping to "haiku" / "sonnet" enables the run and picks
|
|
100
|
+
// the Claude model the archivist CLI spawns. The turn-end hook
|
|
101
|
+
// (`maybeRunJournal` in the agent finally block) and the hourly
|
|
102
|
+
// `system:journal` scheduled task both honour this — off short-
|
|
103
|
+
// circuits before the interval gate is even consulted.
|
|
104
|
+
journal?: JournalMode;
|
|
66
105
|
}
|
|
67
106
|
|
|
68
107
|
const DEFAULT_SETTINGS: AppSettings = { extraAllowedTools: [] };
|
|
@@ -94,6 +133,14 @@ function isEffortLevel(value: unknown): value is EffortLevel {
|
|
|
94
133
|
return typeof value === "string" && (EFFORT_LEVELS as readonly string[]).includes(value);
|
|
95
134
|
}
|
|
96
135
|
|
|
136
|
+
function isChatIndexMode(value: unknown): value is ChatIndexMode {
|
|
137
|
+
return typeof value === "string" && (CHAT_INDEX_MODES as readonly string[]).includes(value);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function isJournalMode(value: unknown): value is JournalMode {
|
|
141
|
+
return typeof value === "string" && (JOURNAL_MODES as readonly string[]).includes(value);
|
|
142
|
+
}
|
|
143
|
+
|
|
97
144
|
function isVoiceInputSettings(value: unknown): value is { enabled: boolean; model?: string } {
|
|
98
145
|
if (!isRecord(value)) return false;
|
|
99
146
|
if (typeof value.enabled !== "boolean") return false;
|
|
@@ -108,9 +155,13 @@ export function isAppSettings(value: unknown): value is AppSettings {
|
|
|
108
155
|
if (value.photoExif !== undefined && !isPhotoExifSettings(value.photoExif)) return false;
|
|
109
156
|
if (value.effortLevel !== undefined && !isEffortLevel(value.effortLevel)) return false;
|
|
110
157
|
if (value.voiceInput !== undefined && !isVoiceInputSettings(value.voiceInput)) return false;
|
|
158
|
+
if (value.chatIndex !== undefined && !isChatIndexMode(value.chatIndex)) return false;
|
|
159
|
+
if (value.journal !== undefined && !isJournalMode(value.journal)) return false;
|
|
111
160
|
return true;
|
|
112
161
|
}
|
|
113
162
|
|
|
163
|
+
// isAppSettingsPatch adds a null sentinel to `chatIndex` / `journal` (see below).
|
|
164
|
+
|
|
114
165
|
/** A PUT-payload validator: every field optional, but if present
|
|
115
166
|
* it must match the AppSettings shape. Distinct from
|
|
116
167
|
* `isAppSettings` (which insists on the full storage shape) so a
|
|
@@ -127,28 +178,46 @@ export function isAppSettings(value: unknown): value is AppSettings {
|
|
|
127
178
|
* but lets nullable fields carry `null` as a "clear me" sentinel —
|
|
128
179
|
* callers normalise via `normaliseAppSettingsPatch` before merging
|
|
129
180
|
* into the storage shape (#1323). */
|
|
130
|
-
export type AppSettingsPatch = Partial<Omit<AppSettings, "effortLevel">> & {
|
|
181
|
+
export type AppSettingsPatch = Partial<Omit<AppSettings, "effortLevel" | "chatIndex" | "journal">> & {
|
|
131
182
|
effortLevel?: EffortLevel | null;
|
|
183
|
+
chatIndex?: ChatIndexMode | null;
|
|
184
|
+
journal?: JournalMode | null;
|
|
132
185
|
};
|
|
133
186
|
|
|
134
187
|
/** Convert a wire patch to the storage-shape patch by dropping any
|
|
135
188
|
* `null` sentinels (which mean "clear" for the corresponding field). */
|
|
136
189
|
export function normaliseAppSettingsPatch(patch: AppSettingsPatch): Partial<AppSettings> {
|
|
137
|
-
const { effortLevel, ...rest } = patch;
|
|
190
|
+
const { effortLevel, chatIndex, journal, ...rest } = patch;
|
|
138
191
|
const out: Partial<AppSettings> = { ...rest };
|
|
139
192
|
if (effortLevel !== null && effortLevel !== undefined) {
|
|
140
193
|
out.effortLevel = effortLevel;
|
|
141
194
|
}
|
|
195
|
+
if (chatIndex !== null && chatIndex !== undefined) {
|
|
196
|
+
out.chatIndex = chatIndex;
|
|
197
|
+
}
|
|
198
|
+
if (journal !== null && journal !== undefined) {
|
|
199
|
+
out.journal = journal;
|
|
200
|
+
}
|
|
142
201
|
return out;
|
|
143
202
|
}
|
|
144
203
|
|
|
204
|
+
// Split each field's optional-with-null-sentinel validation into its own
|
|
205
|
+
// mini-helper so `isAppSettingsPatch` stays under the cognitive-complexity
|
|
206
|
+
// ceiling as new nullable fields land.
|
|
207
|
+
const isOptionalString = (value: unknown): boolean => value === undefined || typeof value === "string";
|
|
208
|
+
const isOptionalNullableEffortLevel = (value: unknown): boolean => value === undefined || value === null || isEffortLevel(value);
|
|
209
|
+
const isOptionalNullableChatIndexMode = (value: unknown): boolean => value === undefined || value === null || isChatIndexMode(value);
|
|
210
|
+
const isOptionalNullableJournalMode = (value: unknown): boolean => value === undefined || value === null || isJournalMode(value);
|
|
211
|
+
|
|
145
212
|
export function isAppSettingsPatch(value: unknown): value is AppSettingsPatch {
|
|
146
213
|
if (!isRecord(value)) return false;
|
|
147
214
|
if (value.extraAllowedTools !== undefined && !isStringArray(value.extraAllowedTools)) return false;
|
|
148
|
-
if (value.googleMapsApiKey
|
|
215
|
+
if (!isOptionalString(value.googleMapsApiKey)) return false;
|
|
149
216
|
if (value.photoExif !== undefined && !isPhotoExifSettings(value.photoExif)) return false;
|
|
150
|
-
if (
|
|
217
|
+
if (!isOptionalNullableEffortLevel(value.effortLevel)) return false;
|
|
151
218
|
if (value.voiceInput !== undefined && !isVoiceInputSettings(value.voiceInput)) return false;
|
|
219
|
+
if (!isOptionalNullableChatIndexMode(value.chatIndex)) return false;
|
|
220
|
+
if (!isOptionalNullableJournalMode(value.journal)) return false;
|
|
152
221
|
return true;
|
|
153
222
|
}
|
|
154
223
|
|
|
@@ -182,9 +251,31 @@ function cloneAppSettings(settings: AppSettings): AppSettings {
|
|
|
182
251
|
copy.voiceInput.model = settings.voiceInput.model;
|
|
183
252
|
}
|
|
184
253
|
}
|
|
254
|
+
if (settings.chatIndex !== undefined) {
|
|
255
|
+
copy.chatIndex = settings.chatIndex;
|
|
256
|
+
}
|
|
257
|
+
if (settings.journal !== undefined) {
|
|
258
|
+
copy.journal = settings.journal;
|
|
259
|
+
}
|
|
185
260
|
return copy;
|
|
186
261
|
}
|
|
187
262
|
|
|
263
|
+
/** Chat-index mode with the documented "undefined → off" default, so
|
|
264
|
+
* every reader (indexer, backfill scheduler, force-startup switch,
|
|
265
|
+
* Settings tab) resolves the same way. Callers switch on the
|
|
266
|
+
* returned literal string to decide skip vs pick a model. */
|
|
267
|
+
export function chatIndexMode(settings: AppSettings): ChatIndexMode {
|
|
268
|
+
return settings.chatIndex ?? "off";
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/** Journal mode with the documented "undefined → off" default, so
|
|
272
|
+
* every reader (turn-end hook, hourly scheduler, force-startup
|
|
273
|
+
* switch, Settings tab) resolves the same way. Callers switch on
|
|
274
|
+
* the returned literal string to decide skip vs pick a model. */
|
|
275
|
+
export function journalMode(settings: AppSettings): JournalMode {
|
|
276
|
+
return settings.journal ?? "off";
|
|
277
|
+
}
|
|
278
|
+
|
|
188
279
|
/** Read the photo-exif auto-capture flag with the documented
|
|
189
280
|
* default of `true`. Centralises the "missing block ⇒ on" rule so
|
|
190
281
|
* the post-save hook + future Settings UI stay aligned. */
|
|
@@ -234,6 +325,12 @@ export function saveSettings(settings: AppSettings): void {
|
|
|
234
325
|
payload.voiceInput.model = settings.voiceInput.model;
|
|
235
326
|
}
|
|
236
327
|
}
|
|
328
|
+
if (settings.chatIndex !== undefined) {
|
|
329
|
+
payload.chatIndex = settings.chatIndex;
|
|
330
|
+
}
|
|
331
|
+
if (settings.journal !== undefined) {
|
|
332
|
+
payload.journal = settings.journal;
|
|
333
|
+
}
|
|
237
334
|
const serialised = JSON.stringify(payload, null, 2);
|
|
238
335
|
writeFileAtomicSync(settingsPath(), `${serialised}\n`, { mode: 0o600 });
|
|
239
336
|
}
|