volute 0.32.0 → 0.33.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.
- package/README.md +16 -0
- package/dist/{activity-events-HETAODOK.js → activity-events-XJO3P4RR.js} +1 -1
- package/dist/{ai-service-ZIPCV3MX.js → ai-service-SBY2WG7O.js} +2 -2
- package/dist/api.d.ts +666 -848
- package/dist/{auth-6DMGES3I.js → auth-GKCDSO4T.js} +2 -2
- package/dist/{chat-XT4OBJBU.js → chat-U5ZOME3O.js} +8 -8
- package/dist/{chunk-QBQ424EM.js → chunk-3Z2DPESO.js} +457 -203
- package/dist/chunk-6LXAAQ43.js +22 -0
- package/dist/{spirit-N4W4UQRH.js → chunk-7J3HEVR7.js} +12 -9
- package/dist/{chunk-WKF5FEFK.js → chunk-A2A4KLFE.js} +54 -155
- package/dist/{chunk-D5G5YOPL.js → chunk-C7I35G4R.js} +3 -3
- package/dist/{chunk-SX5TKJBZ.js → chunk-GY5HBI7A.js} +1 -1
- package/dist/{chunk-2FLJ63GU.js → chunk-JUKK7FPS.js} +1 -1
- package/dist/{chunk-TDRYEPH4.js → chunk-JYVGHWEJ.js} +2 -2
- package/dist/chunk-KIEPMIM5.js +59 -0
- package/dist/{chunk-QZANELPX.js → chunk-KVK2DLWI.js} +1 -0
- package/dist/{chunk-R7E6CRVQ.js → chunk-LOEJ4HPQ.js} +1 -1
- package/dist/{chunk-TSXLLQZW.js → chunk-N432I7QH.js} +9 -0
- package/dist/{chunk-LSGWR54X.js → chunk-NNB4WIG7.js} +1 -1
- package/dist/{chunk-JJ7W6WSB.js → chunk-NPKSDYA2.js} +2 -2
- package/dist/chunk-OYAKCAVY.js +29 -0
- package/dist/{chunk-IYDIE3HG.js → chunk-QTUVYI7W.js} +1 -1
- package/dist/{chunk-S6NFERDC.js → chunk-RVGLDGMI.js} +1 -1
- package/dist/{chunk-LGB6JBHI.js → chunk-VH33ZWMW.js} +4 -54
- package/dist/cli.js +26 -18
- package/dist/{clock-2UOZ6JPU.js → clock-BVH3V6E3.js} +5 -5
- package/dist/{cloud-sync-JN3NWKEM.js → cloud-sync-4NWLMFVH.js} +15 -11
- package/dist/{conversations-3O5O6AS3.js → conversations-AWI5SZW2.js} +2 -2
- package/dist/{create-WBBYI6V7.js → create-2FK7Z46Y.js} +1 -1
- package/dist/{create-RNLNCORE.js → create-YWD2TIP4.js} +4 -4
- package/dist/{daemon-restart-NGFHFAUF.js → daemon-restart-GOBUKLX7.js} +6 -5
- package/dist/daemon.js +1182 -1031
- package/dist/delivery-manager-PFAKEJTC.js +32 -0
- package/dist/{down-TB3ESMNP.js → down-FWWTEKXM.js} +4 -3
- package/dist/{extension-FQ5D3NCC.js → extension-OBTGKQQD.js} +2 -1
- package/dist/{extensions-GDYWQXC4.js → extensions-KYNTVTMO.js} +7 -6
- package/dist/isolation-LLAYQYDY.js +22 -0
- package/dist/message-delivery-DFF5SJRM.js +42 -0
- package/dist/{mind-2B6M7Y25.js → mind-IOJFLEM5.js} +13 -7
- package/dist/{mind-activity-tracker-NZZT2NTT.js → mind-activity-tracker-F6O4Q2SL.js} +2 -2
- package/dist/mind-manager-NBJF5D26.js +32 -0
- package/dist/mind-profile-P67FEHOY.js +47 -0
- package/dist/mind-service-2MQ6UK5N.js +38 -0
- package/dist/{package-PK6JUFL3.js → package-U3VFO273.js} +2 -1
- package/dist/read-stdin-HQJ7774D.js +8 -0
- package/dist/{sandbox-JANNTX6U.js → sandbox-GJOK4QLQ.js} +2 -2
- package/dist/scheduler-ZZ7XGQG6.js +32 -0
- package/dist/seed-QDYVLG74.js +11 -0
- package/dist/seed-check-S2IX25RL.js +32 -0
- package/dist/seed-cmd-DKOUFEAU.js +36 -0
- package/dist/{seed-ALUQ55FF.js → seed-create-4XBBOLRH.js} +5 -5
- package/dist/{sprout-L2GFOVF7.js → seed-sprout-GQEIIQRT.js} +19 -6
- package/dist/{send-3MI36LEF.js → send-QIV2INHB.js} +51 -49
- package/dist/{setup-SZIARWI6.js → setup-TISPCO22.js} +3 -1
- package/dist/{setup-WENLVPVP.js → setup-XMCBE3LF.js} +7 -5
- package/dist/skills/imagegen/SKILL.md +11 -7
- package/dist/skills/imagegen/scripts/imagegen.ts +146 -25
- package/dist/skills/orientation/SKILL.md +9 -2
- package/dist/skills/seed-nurture/SKILL.md +42 -0
- package/dist/skills/volute-mind/SKILL.md +4 -0
- package/dist/{skills-XNZK6P4K.js → skills-7FV7EJTE.js} +4 -3
- package/dist/sleep-manager-JTXSN7NV.js +36 -0
- package/dist/spirit-VRONKFMF.js +23 -0
- package/dist/sprout-WKLZXUIQ.js +11 -0
- package/dist/{status-TCUMUO6M.js → status-3JBTFSMI.js} +3 -2
- package/dist/{system-chat-NPYFYZVI.js → system-chat-JAPOJ3KE.js} +15 -11
- package/dist/{systems-DHBKVYEY.js → systems-XRI52VCH.js} +2 -2
- package/dist/{up-6I6BHRTO.js → up-M5AS6SBV.js} +5 -4
- package/dist/{update-QVPRF6GR.js → update-UD543CXX.js} +3 -2
- package/dist/{version-notify-TCKWBZZG.js → version-notify-NBI2MTJO.js} +18 -15
- package/dist/volute-config-HD7WWUQC.js +10 -0
- package/dist/web-assets/assets/index-CWJrVveV.css +1 -0
- package/dist/web-assets/assets/index-DJt14FRI.js +75 -0
- package/dist/web-assets/index.html +2 -2
- package/package.json +2 -1
- package/templates/claude/src/lib/stream-consumer.ts +38 -0
- package/templates/codex/src/agent.ts +1 -0
- package/dist/delivery-manager-SDVXFD4W.js +0 -28
- package/dist/message-delivery-2FIM7QKO.js +0 -32
- package/dist/mind-manager-BNCMGYXW.js +0 -28
- package/dist/mind-service-AV273WT4.js +0 -34
- package/dist/sleep-manager-53DZOWW7.js +0 -32
- package/dist/web-assets/assets/index-Bui7U9Uu.css +0 -1
- package/dist/web-assets/assets/index-e36DIo1b.js +0 -73
- package/dist/{accept-74M7I4RZ.js → accept-D5VBM7JW.js} +3 -3
- package/dist/{bridge-BVCBTGPF.js → bridge-TXWWPPOJ.js} +3 -3
- package/dist/{env-RLYQBOOP.js → env-JCOF2222.js} +3 -3
- package/dist/{files-EAMPO2SJ.js → files-65PMW5IK.js} +3 -3
- package/dist/{history-FO5PHBQ5.js → history-DKCDI3JO.js} +3 -3
- package/dist/{list-DW2VRTOZ.js → list-JQ463EDA.js} +3 -3
- package/dist/{login-7CHPW2PN.js → login-D7ETSU4R.js} +3 -3
- package/dist/{mind-sleep-B7BHJLH7.js → mind-sleep-WW2IX7JT.js} +3 -3
- package/dist/{mind-wake-GY3RFX7Y.js → mind-wake-VSSGW465.js} +3 -3
- package/dist/{read-5AMJRO3D.js → read-EBY56C33.js} +3 -3
- package/dist/{register-V2JZZKFK.js → register-HD74C4TT.js} +3 -3
- package/dist/{reject-33HEZMZ4.js → reject-UJKFBHRO.js} +3 -3
- package/dist/{skill-TUVOTW4Z.js → skill-PSQGRRJX.js} +3 -3
package/dist/daemon.js
CHANGED
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
computeTemplateHash
|
|
4
|
-
} from "./chunk-PVY5W6QN.js";
|
|
5
2
|
import {
|
|
6
3
|
checkForUpdate,
|
|
7
4
|
checkForUpdateCached,
|
|
8
5
|
getCurrentVersion
|
|
9
6
|
} from "./chunk-M7UL5S3Q.js";
|
|
7
|
+
import {
|
|
8
|
+
computeTemplateHash
|
|
9
|
+
} from "./chunk-PVY5W6QN.js";
|
|
10
10
|
import {
|
|
11
11
|
PROMPT_DEFAULTS,
|
|
12
12
|
PROMPT_KEYS,
|
|
13
13
|
RestartTracker,
|
|
14
14
|
RotatingLog,
|
|
15
15
|
announceToSystem,
|
|
16
|
+
assignSession,
|
|
17
|
+
completeOrphanedTurns,
|
|
18
|
+
completeTurn,
|
|
19
|
+
createTurn,
|
|
16
20
|
deliverMessage,
|
|
17
21
|
ensureSystemChannel,
|
|
18
22
|
generateSystemReply,
|
|
23
|
+
getActiveTurnId,
|
|
19
24
|
getDeliveryManager,
|
|
25
|
+
getLastToolUseEventId,
|
|
20
26
|
getMindManager,
|
|
21
27
|
getMindPromptDefaults,
|
|
22
28
|
getPrompt,
|
|
@@ -33,24 +39,30 @@ import {
|
|
|
33
39
|
initTokenBudget,
|
|
34
40
|
isConversationId,
|
|
35
41
|
joinSystemChannel,
|
|
42
|
+
linkToolResultToTurn,
|
|
36
43
|
publish as publish2,
|
|
37
44
|
publishTypingForChannels,
|
|
38
|
-
readVoluteConfig,
|
|
39
45
|
recordInbound,
|
|
46
|
+
recordOutbound,
|
|
40
47
|
resolveMindToken,
|
|
48
|
+
setSummaryEventId,
|
|
41
49
|
startMindFull,
|
|
42
50
|
stopMindFull,
|
|
43
51
|
subscribe as subscribe3,
|
|
52
|
+
subscribeAll,
|
|
44
53
|
substitute,
|
|
45
54
|
tagUntaggedInbound,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
55
|
+
tagUntaggedOutbound,
|
|
56
|
+
trackToolUse
|
|
57
|
+
} from "./chunk-3Z2DPESO.js";
|
|
58
|
+
import {
|
|
59
|
+
extractTextContent
|
|
60
|
+
} from "./chunk-SKLSMHXO.js";
|
|
49
61
|
import {
|
|
50
62
|
getActiveMinds,
|
|
51
63
|
onMindEvent,
|
|
52
64
|
stopAll
|
|
53
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-LOEJ4HPQ.js";
|
|
54
66
|
import {
|
|
55
67
|
addMessage,
|
|
56
68
|
createChannel,
|
|
@@ -78,8 +90,8 @@ import {
|
|
|
78
90
|
publish,
|
|
79
91
|
setConversationPrivate,
|
|
80
92
|
subscribe as subscribe2
|
|
81
|
-
} from "./chunk-
|
|
82
|
-
import "./chunk-
|
|
93
|
+
} from "./chunk-RVGLDGMI.js";
|
|
94
|
+
import "./chunk-GY5HBI7A.js";
|
|
83
95
|
import {
|
|
84
96
|
acceptPending,
|
|
85
97
|
formatFileSize,
|
|
@@ -88,6 +100,18 @@ import {
|
|
|
88
100
|
stageFile,
|
|
89
101
|
validateFilePath
|
|
90
102
|
} from "./chunk-ALEF47VT.js";
|
|
103
|
+
import "./chunk-7J3HEVR7.js";
|
|
104
|
+
import {
|
|
105
|
+
applyInitFiles,
|
|
106
|
+
composeTemplate,
|
|
107
|
+
copyTemplateToDir,
|
|
108
|
+
findTemplatesRoot,
|
|
109
|
+
listFiles
|
|
110
|
+
} from "./chunk-G53F3JA4.js";
|
|
111
|
+
import {
|
|
112
|
+
readVoluteConfig,
|
|
113
|
+
writeVoluteConfig
|
|
114
|
+
} from "./chunk-OYAKCAVY.js";
|
|
91
115
|
import {
|
|
92
116
|
findBridgeForChannel,
|
|
93
117
|
findOpenClawSession,
|
|
@@ -110,23 +134,18 @@ import {
|
|
|
110
134
|
writeEnv
|
|
111
135
|
} from "./chunk-2NGTS5UU.js";
|
|
112
136
|
import {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
createTurn,
|
|
137
|
+
isHomeOnlyArchive
|
|
138
|
+
} from "./chunk-I5KY25PQ.js";
|
|
139
|
+
import {
|
|
117
140
|
deleteSystemsConfig,
|
|
118
|
-
getActiveTurnId,
|
|
119
141
|
getExtensionStandardSkills,
|
|
120
|
-
getLastToolUseEventId,
|
|
121
142
|
getLoadedExtensions,
|
|
122
143
|
loadAllExtensions,
|
|
123
144
|
notifyExtensionsDaemonStart,
|
|
124
145
|
notifyExtensionsDaemonStop,
|
|
125
146
|
readSystemsConfig,
|
|
126
|
-
setSummaryEventId,
|
|
127
|
-
trackToolUse,
|
|
128
147
|
writeSystemsConfig
|
|
129
|
-
} from "./chunk-
|
|
148
|
+
} from "./chunk-A2A4KLFE.js";
|
|
130
149
|
import "./chunk-PB65JZK2.js";
|
|
131
150
|
import {
|
|
132
151
|
approveUser,
|
|
@@ -145,18 +164,11 @@ import {
|
|
|
145
164
|
setUserRole,
|
|
146
165
|
updateUserProfile,
|
|
147
166
|
verifyUser
|
|
148
|
-
} from "./chunk-
|
|
167
|
+
} from "./chunk-JYVGHWEJ.js";
|
|
149
168
|
import {
|
|
150
169
|
broadcast,
|
|
151
170
|
subscribe
|
|
152
|
-
} from "./chunk-
|
|
153
|
-
import {
|
|
154
|
-
applyInitFiles,
|
|
155
|
-
composeTemplate,
|
|
156
|
-
copyTemplateToDir,
|
|
157
|
-
findTemplatesRoot,
|
|
158
|
-
listFiles
|
|
159
|
-
} from "./chunk-G53F3JA4.js";
|
|
171
|
+
} from "./chunk-KVK2DLWI.js";
|
|
160
172
|
import {
|
|
161
173
|
SEED_SKILLS,
|
|
162
174
|
STANDARD_SKILLS,
|
|
@@ -174,16 +186,14 @@ import {
|
|
|
174
186
|
syncBuiltinSkills,
|
|
175
187
|
uninstallSkill,
|
|
176
188
|
updateSkill
|
|
177
|
-
} from "./chunk-
|
|
178
|
-
import {
|
|
179
|
-
isHomeOnlyArchive
|
|
180
|
-
} from "./chunk-I5KY25PQ.js";
|
|
189
|
+
} from "./chunk-C7I35G4R.js";
|
|
181
190
|
import {
|
|
182
191
|
aiCompleteUtility,
|
|
183
192
|
getAiConfig,
|
|
184
193
|
getAvailableModels,
|
|
185
194
|
getConfiguredProviders,
|
|
186
195
|
getEnabledModels,
|
|
196
|
+
getUtilityModel,
|
|
187
197
|
qualifyModelId,
|
|
188
198
|
removeAiConfig,
|
|
189
199
|
removeProviderConfig,
|
|
@@ -191,33 +201,36 @@ import {
|
|
|
191
201
|
resolveTemplate,
|
|
192
202
|
saveProviderConfig,
|
|
193
203
|
setEnabledModels,
|
|
204
|
+
setUtilityModel,
|
|
194
205
|
unqualifyModelId
|
|
195
|
-
} from "./chunk-
|
|
206
|
+
} from "./chunk-QTUVYI7W.js";
|
|
196
207
|
import {
|
|
197
208
|
logBuffer,
|
|
198
209
|
logger_default
|
|
199
210
|
} from "./chunk-YUIHSKR6.js";
|
|
211
|
+
import {
|
|
212
|
+
exec,
|
|
213
|
+
gitExec,
|
|
214
|
+
resolveVoluteBin
|
|
215
|
+
} from "./chunk-KIEPMIM5.js";
|
|
200
216
|
import {
|
|
201
217
|
chownMindDir,
|
|
202
218
|
createMindUser,
|
|
203
219
|
deleteMindUser,
|
|
204
220
|
ensureVoluteGroup,
|
|
205
|
-
exec,
|
|
206
|
-
gitExec,
|
|
207
221
|
isIsolationEnabled,
|
|
208
222
|
mindUserName,
|
|
209
|
-
resolveVoluteBin,
|
|
210
223
|
wrapForIsolation
|
|
211
|
-
} from "./chunk-
|
|
224
|
+
} from "./chunk-VH33ZWMW.js";
|
|
212
225
|
import {
|
|
213
226
|
isSetupComplete,
|
|
214
227
|
readGlobalConfig,
|
|
215
228
|
writeGlobalConfig
|
|
216
|
-
} from "./chunk-
|
|
217
|
-
import "./chunk-D424ZQGI.js";
|
|
229
|
+
} from "./chunk-N432I7QH.js";
|
|
218
230
|
import {
|
|
219
231
|
readSessionFile
|
|
220
232
|
} from "./chunk-UKVWJRKN.js";
|
|
233
|
+
import "./chunk-D424ZQGI.js";
|
|
221
234
|
import {
|
|
222
235
|
buildVoluteSlug,
|
|
223
236
|
slugify
|
|
@@ -247,7 +260,6 @@ import {
|
|
|
247
260
|
} from "./chunk-LRCG2JLP.js";
|
|
248
261
|
import {
|
|
249
262
|
activity,
|
|
250
|
-
conversationParticipants,
|
|
251
263
|
conversations,
|
|
252
264
|
messages,
|
|
253
265
|
mindHistory,
|
|
@@ -768,6 +780,201 @@ import { Hono } from "hono";
|
|
|
768
780
|
import { streamSSE } from "hono/streaming";
|
|
769
781
|
import { z } from "zod";
|
|
770
782
|
|
|
783
|
+
// src/lib/services/imagegen.ts
|
|
784
|
+
import Replicate from "replicate";
|
|
785
|
+
var PROVIDERS = {
|
|
786
|
+
replicate: {
|
|
787
|
+
envVar: "REPLICATE_API_TOKEN",
|
|
788
|
+
generate: replicateGenerate,
|
|
789
|
+
search: replicateSearch
|
|
790
|
+
},
|
|
791
|
+
openrouter: {
|
|
792
|
+
envVar: "OPENROUTER_API_KEY",
|
|
793
|
+
generate: openrouterGenerate,
|
|
794
|
+
search: openrouterSearch
|
|
795
|
+
}
|
|
796
|
+
};
|
|
797
|
+
async function replicateGenerate(model, prompt, apiKey) {
|
|
798
|
+
const replicate = new Replicate({ auth: apiKey });
|
|
799
|
+
const output = await replicate.run(model, {
|
|
800
|
+
input: { prompt }
|
|
801
|
+
});
|
|
802
|
+
const file = Array.isArray(output) ? output[0] : output;
|
|
803
|
+
if (!file) throw new Error(`Model ${model} returned no output`);
|
|
804
|
+
if (typeof file === "string") {
|
|
805
|
+
const res = await fetch(file);
|
|
806
|
+
if (!res.ok) throw new Error(`Failed to fetch image from URL: ${res.status}`);
|
|
807
|
+
return Buffer.from(await res.arrayBuffer());
|
|
808
|
+
}
|
|
809
|
+
const chunks = [];
|
|
810
|
+
for await (const chunk of file) {
|
|
811
|
+
chunks.push(chunk);
|
|
812
|
+
}
|
|
813
|
+
return Buffer.concat(chunks);
|
|
814
|
+
}
|
|
815
|
+
async function replicateSearch(query, apiKey) {
|
|
816
|
+
const replicate = new Replicate({ auth: apiKey });
|
|
817
|
+
const response = await replicate.models.search(query || "text to image");
|
|
818
|
+
return response.results.slice(0, 20).map((m) => ({
|
|
819
|
+
id: `replicate:${m.owner}/${m.name}`,
|
|
820
|
+
name: m.name,
|
|
821
|
+
description: m.description?.slice(0, 200),
|
|
822
|
+
owner: m.owner
|
|
823
|
+
}));
|
|
824
|
+
}
|
|
825
|
+
async function openrouterGenerate(model, prompt, apiKey) {
|
|
826
|
+
const res = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
|
827
|
+
method: "POST",
|
|
828
|
+
headers: {
|
|
829
|
+
"Content-Type": "application/json",
|
|
830
|
+
Authorization: `Bearer ${apiKey}`
|
|
831
|
+
},
|
|
832
|
+
body: JSON.stringify({
|
|
833
|
+
model,
|
|
834
|
+
messages: [{ role: "user", content: prompt }],
|
|
835
|
+
modalities: ["image", "text"]
|
|
836
|
+
})
|
|
837
|
+
});
|
|
838
|
+
if (!res.ok) {
|
|
839
|
+
const body = await res.text().catch(() => `HTTP ${res.status}`);
|
|
840
|
+
throw new Error(`OpenRouter API error: ${body}`);
|
|
841
|
+
}
|
|
842
|
+
const data = await res.json();
|
|
843
|
+
const imageUrl = data.choices?.[0]?.message?.images?.[0]?.image_url?.url;
|
|
844
|
+
if (!imageUrl) throw new Error("OpenRouter returned no image");
|
|
845
|
+
const match = imageUrl.match(/^data:[^;]+;base64,(.+)$/);
|
|
846
|
+
if (match) {
|
|
847
|
+
return Buffer.from(match[1], "base64");
|
|
848
|
+
}
|
|
849
|
+
const imgRes = await fetch(imageUrl);
|
|
850
|
+
if (!imgRes.ok) throw new Error(`Failed to fetch image from URL: ${imgRes.status}`);
|
|
851
|
+
return Buffer.from(await imgRes.arrayBuffer());
|
|
852
|
+
}
|
|
853
|
+
async function openrouterSearch(query, _apiKey) {
|
|
854
|
+
const res = await fetch("https://openrouter.ai/api/v1/models?output_modalities=image");
|
|
855
|
+
if (!res.ok) throw new Error(`OpenRouter model list failed: ${res.status}`);
|
|
856
|
+
const data = await res.json();
|
|
857
|
+
const models = data.data ?? [];
|
|
858
|
+
const q = query.toLowerCase();
|
|
859
|
+
const filtered = models.filter((m) => m.id.toLowerCase().includes(q) || m.name.toLowerCase().includes(q)).slice(0, 20);
|
|
860
|
+
return filtered.map((m) => {
|
|
861
|
+
const parts = m.id.split("/");
|
|
862
|
+
return {
|
|
863
|
+
id: `openrouter:${m.id}`,
|
|
864
|
+
name: m.name,
|
|
865
|
+
description: m.description?.slice(0, 200),
|
|
866
|
+
owner: parts[0] || m.id
|
|
867
|
+
};
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
function parseModelId(prefixedId) {
|
|
871
|
+
const colonIdx = prefixedId.indexOf(":");
|
|
872
|
+
if (colonIdx === -1)
|
|
873
|
+
throw new Error(
|
|
874
|
+
`Model ID must be provider-prefixed (e.g. replicate:owner/model), got: ${prefixedId}`
|
|
875
|
+
);
|
|
876
|
+
const provider = prefixedId.slice(0, colonIdx);
|
|
877
|
+
const model = prefixedId.slice(colonIdx + 1);
|
|
878
|
+
if (!PROVIDERS[provider]) throw new Error(`Unknown imagegen provider: ${provider}`);
|
|
879
|
+
return { provider, model };
|
|
880
|
+
}
|
|
881
|
+
function getImagegenConfig() {
|
|
882
|
+
return readGlobalConfig().imagegen ?? null;
|
|
883
|
+
}
|
|
884
|
+
function updateImagegenConfig(fn) {
|
|
885
|
+
const config2 = readGlobalConfig();
|
|
886
|
+
const ig = config2.imagegen ?? {};
|
|
887
|
+
fn(ig);
|
|
888
|
+
config2.imagegen = ig;
|
|
889
|
+
writeGlobalConfig(config2);
|
|
890
|
+
}
|
|
891
|
+
function saveProviderConfig2(id, apiKey) {
|
|
892
|
+
if (!PROVIDERS[id]) throw new Error(`Unknown imagegen provider: ${id}`);
|
|
893
|
+
updateImagegenConfig((ig) => {
|
|
894
|
+
ig.providers = ig.providers ?? {};
|
|
895
|
+
ig.providers[id] = { apiKey };
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
function removeProviderConfig2(id) {
|
|
899
|
+
if (!PROVIDERS[id]) throw new Error(`Unknown imagegen provider: ${id}`);
|
|
900
|
+
updateImagegenConfig((ig) => {
|
|
901
|
+
if (ig.providers) {
|
|
902
|
+
delete ig.providers[id];
|
|
903
|
+
if (Object.keys(ig.providers).length === 0) delete ig.providers;
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
function getConfiguredProviders2() {
|
|
908
|
+
const config2 = readGlobalConfig();
|
|
909
|
+
const ig = config2.imagegen ?? null;
|
|
910
|
+
const aiProviders = config2.ai?.providers;
|
|
911
|
+
return Object.entries(PROVIDERS).map(([id, { envVar }]) => {
|
|
912
|
+
const providerConfig = ig?.providers?.[id];
|
|
913
|
+
if (providerConfig?.apiKey) return { id, configured: true, authMethod: "api_key" };
|
|
914
|
+
if (aiProviders?.[id]?.apiKey) return { id, configured: true, authMethod: "api_key" };
|
|
915
|
+
if (process.env[envVar]) return { id, configured: true, authMethod: "env_var" };
|
|
916
|
+
return { id, configured: false, authMethod: null };
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
function resolveApiKey2(providerId) {
|
|
920
|
+
const provider = PROVIDERS[providerId];
|
|
921
|
+
if (!provider) return void 0;
|
|
922
|
+
const config2 = readGlobalConfig();
|
|
923
|
+
const configKey = config2.imagegen?.providers?.[providerId]?.apiKey;
|
|
924
|
+
if (configKey) return configKey;
|
|
925
|
+
const aiKey = config2.ai?.providers?.[providerId]?.apiKey;
|
|
926
|
+
if (aiKey) return aiKey;
|
|
927
|
+
return process.env[provider.envVar] || void 0;
|
|
928
|
+
}
|
|
929
|
+
function getEnabledModels2() {
|
|
930
|
+
return getImagegenConfig()?.models ?? [];
|
|
931
|
+
}
|
|
932
|
+
function setEnabledModels2(ids) {
|
|
933
|
+
updateImagegenConfig((ig) => {
|
|
934
|
+
ig.models = ids;
|
|
935
|
+
if (ig.defaultModel && !ids.includes(ig.defaultModel)) {
|
|
936
|
+
delete ig.defaultModel;
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
function getDefaultModel() {
|
|
941
|
+
return getImagegenConfig()?.defaultModel;
|
|
942
|
+
}
|
|
943
|
+
function setDefaultModel(modelId) {
|
|
944
|
+
updateImagegenConfig((ig) => {
|
|
945
|
+
if (modelId) {
|
|
946
|
+
ig.defaultModel = modelId;
|
|
947
|
+
} else {
|
|
948
|
+
delete ig.defaultModel;
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
async function searchModels(query, provider) {
|
|
953
|
+
if (provider) {
|
|
954
|
+
const providerDef = PROVIDERS[provider];
|
|
955
|
+
if (!providerDef) throw new Error(`Unknown imagegen provider: ${provider}`);
|
|
956
|
+
const apiKey = resolveApiKey2(provider);
|
|
957
|
+
if (!apiKey) throw new Error(`No ${provider} API key configured`);
|
|
958
|
+
return providerDef.search(query || "text to image", apiKey);
|
|
959
|
+
}
|
|
960
|
+
const searches = [];
|
|
961
|
+
for (const [id, def] of Object.entries(PROVIDERS)) {
|
|
962
|
+
const apiKey = resolveApiKey2(id);
|
|
963
|
+
if (!apiKey) continue;
|
|
964
|
+
searches.push(def.search(query || "text to image", apiKey).catch(() => []));
|
|
965
|
+
}
|
|
966
|
+
if (searches.length === 0) {
|
|
967
|
+
throw new Error("No imagegen providers configured");
|
|
968
|
+
}
|
|
969
|
+
return (await Promise.all(searches)).flat();
|
|
970
|
+
}
|
|
971
|
+
async function generateImage(model, prompt) {
|
|
972
|
+
const { provider: providerId, model: modelId } = parseModelId(model);
|
|
973
|
+
const apiKey = resolveApiKey2(providerId);
|
|
974
|
+
if (!apiKey) throw new Error(`No ${providerId} API key configured`);
|
|
975
|
+
return PROVIDERS[providerId].generate(modelId, prompt, apiKey);
|
|
976
|
+
}
|
|
977
|
+
|
|
771
978
|
// src/web/middleware/auth.ts
|
|
772
979
|
import { timingSafeEqual } from "crypto";
|
|
773
980
|
import { eq as eq2, lt as lt2 } from "drizzle-orm";
|
|
@@ -902,6 +1109,7 @@ var requireSelf = (paramName = "name") => createMiddleware(async (c, next) => {
|
|
|
902
1109
|
|
|
903
1110
|
// src/web/api/system.ts
|
|
904
1111
|
var DEFAULT_API_URL = "https://volute.systems";
|
|
1112
|
+
var igLog = logger_default.child("imagegen");
|
|
905
1113
|
var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
906
1114
|
setTimeout(() => process.exit(1), 200);
|
|
907
1115
|
return c.json({ ok: true });
|
|
@@ -1044,7 +1252,77 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1044
1252
|
} catch (err) {
|
|
1045
1253
|
return c.json({ error: `Connection failed: ${err.message}` }, 502);
|
|
1046
1254
|
}
|
|
1047
|
-
}).get("/
|
|
1255
|
+
}).get("/imagegen/providers", requireAdmin, (c) => {
|
|
1256
|
+
return c.json(getConfiguredProviders2());
|
|
1257
|
+
}).put(
|
|
1258
|
+
"/imagegen/providers/:id",
|
|
1259
|
+
requireAdmin,
|
|
1260
|
+
zValidator("json", z.object({ apiKey: z.string().min(1) })),
|
|
1261
|
+
(c) => {
|
|
1262
|
+
const id = c.req.param("id");
|
|
1263
|
+
const { apiKey } = c.req.valid("json");
|
|
1264
|
+
try {
|
|
1265
|
+
saveProviderConfig2(id, apiKey);
|
|
1266
|
+
} catch (err) {
|
|
1267
|
+
return c.json({ error: err instanceof Error ? err.message : "Failed to save" }, 400);
|
|
1268
|
+
}
|
|
1269
|
+
return c.json({ ok: true });
|
|
1270
|
+
}
|
|
1271
|
+
).delete("/imagegen/providers/:id", requireAdmin, (c) => {
|
|
1272
|
+
try {
|
|
1273
|
+
removeProviderConfig2(c.req.param("id"));
|
|
1274
|
+
} catch (err) {
|
|
1275
|
+
return c.json({ error: err instanceof Error ? err.message : "Failed to remove" }, 400);
|
|
1276
|
+
}
|
|
1277
|
+
return c.json({ ok: true });
|
|
1278
|
+
}).get("/imagegen/models", requireAdmin, (c) => {
|
|
1279
|
+
const models = getEnabledModels2();
|
|
1280
|
+
const defaultModel = getDefaultModel();
|
|
1281
|
+
return c.json({ models, defaultModel: defaultModel ?? null });
|
|
1282
|
+
}).put(
|
|
1283
|
+
"/imagegen/models",
|
|
1284
|
+
requireAdmin,
|
|
1285
|
+
zValidator(
|
|
1286
|
+
"json",
|
|
1287
|
+
z.object({ models: z.array(z.string()), defaultModel: z.string().nullable().optional() })
|
|
1288
|
+
),
|
|
1289
|
+
(c) => {
|
|
1290
|
+
const { models, defaultModel } = c.req.valid("json");
|
|
1291
|
+
setEnabledModels2(models);
|
|
1292
|
+
if (defaultModel !== void 0) {
|
|
1293
|
+
setDefaultModel(defaultModel ?? void 0);
|
|
1294
|
+
}
|
|
1295
|
+
return c.json({ ok: true });
|
|
1296
|
+
}
|
|
1297
|
+
).get("/imagegen/models/search", async (c) => {
|
|
1298
|
+
const q = c.req.query("q");
|
|
1299
|
+
const provider = c.req.query("provider");
|
|
1300
|
+
try {
|
|
1301
|
+
const results = await searchModels(q || void 0, provider || void 0);
|
|
1302
|
+
return c.json(results);
|
|
1303
|
+
} catch (err) {
|
|
1304
|
+
igLog.error("model search failed", logger_default.errorData(err));
|
|
1305
|
+
return c.json({ error: err instanceof Error ? err.message : "Search failed" }, 500);
|
|
1306
|
+
}
|
|
1307
|
+
}).post(
|
|
1308
|
+
"/imagegen/generate",
|
|
1309
|
+
zValidator("json", z.object({ model: z.string().min(1), prompt: z.string().min(1) })),
|
|
1310
|
+
async (c) => {
|
|
1311
|
+
const { model, prompt } = c.req.valid("json");
|
|
1312
|
+
try {
|
|
1313
|
+
const buf = await generateImage(model, prompt);
|
|
1314
|
+
return new Response(buf, {
|
|
1315
|
+
headers: {
|
|
1316
|
+
"Content-Type": "image/png",
|
|
1317
|
+
"Content-Length": String(buf.length)
|
|
1318
|
+
}
|
|
1319
|
+
});
|
|
1320
|
+
} catch (err) {
|
|
1321
|
+
igLog.error("image generation failed", logger_default.errorData(err));
|
|
1322
|
+
return c.json({ error: err instanceof Error ? err.message : "Generation failed" }, 500);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
).get("/ai/key/:provider", requireAdmin, async (c) => {
|
|
1048
1326
|
const key = getCachedApiKey(c.req.param("provider"));
|
|
1049
1327
|
if (!key) return c.json({ error: "No key available" }, 404);
|
|
1050
1328
|
return c.json({ key });
|
|
@@ -1092,6 +1370,30 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1092
1370
|
setEnabledModels(models);
|
|
1093
1371
|
return c.json({ ok: true });
|
|
1094
1372
|
}
|
|
1373
|
+
).get("/ai/defaults", requireAdmin, (c) => {
|
|
1374
|
+
const config2 = readGlobalConfig();
|
|
1375
|
+
return c.json({
|
|
1376
|
+
spiritModel: config2.spiritModel ?? null,
|
|
1377
|
+
utilityModel: getUtilityModel() ?? null
|
|
1378
|
+
});
|
|
1379
|
+
}).put(
|
|
1380
|
+
"/ai/defaults",
|
|
1381
|
+
requireAdmin,
|
|
1382
|
+
zValidator(
|
|
1383
|
+
"json",
|
|
1384
|
+
z.object({
|
|
1385
|
+
spiritModel: z.string().nullable(),
|
|
1386
|
+
utilityModel: z.string().nullable()
|
|
1387
|
+
})
|
|
1388
|
+
),
|
|
1389
|
+
(c) => {
|
|
1390
|
+
const { spiritModel, utilityModel } = c.req.valid("json");
|
|
1391
|
+
const config2 = readGlobalConfig();
|
|
1392
|
+
config2.spiritModel = spiritModel ?? void 0;
|
|
1393
|
+
writeGlobalConfig(config2);
|
|
1394
|
+
setUtilityModel(utilityModel ?? void 0);
|
|
1395
|
+
return c.json({ ok: true });
|
|
1396
|
+
}
|
|
1095
1397
|
).put(
|
|
1096
1398
|
"/ai/providers/:id",
|
|
1097
1399
|
requireAdmin,
|
|
@@ -1255,7 +1557,7 @@ function stopApiKeyRefresh() {
|
|
|
1255
1557
|
var system_default = app;
|
|
1256
1558
|
|
|
1257
1559
|
// src/web/app.ts
|
|
1258
|
-
import { Hono as
|
|
1560
|
+
import { Hono as Hono30 } from "hono";
|
|
1259
1561
|
import { bodyLimit } from "hono/body-limit";
|
|
1260
1562
|
import { csrf } from "hono/csrf";
|
|
1261
1563
|
import { HTTPException } from "hono/http-exception";
|
|
@@ -1718,7 +2020,7 @@ var app4 = new Hono4().post("/:platform/inbound", zValidator3("json", inboundSch
|
|
|
1718
2020
|
}
|
|
1719
2021
|
const participants = await getParticipants(channel.id);
|
|
1720
2022
|
if (!participants.some((p) => p.userId === puppet.id)) {
|
|
1721
|
-
const { addParticipant } = await import("./conversations-
|
|
2023
|
+
const { addParticipant } = await import("./conversations-AWI5SZW2.js");
|
|
1722
2024
|
await addParticipant(channel.id, puppet.id);
|
|
1723
2025
|
}
|
|
1724
2026
|
const contentBlocks = body.content;
|
|
@@ -1811,8 +2113,8 @@ async function fanOutToBridgedMinds(opts) {
|
|
|
1811
2113
|
const participants = await getParticipants(opts.conversationId);
|
|
1812
2114
|
const mindParticipants = participants.filter((p) => p.userType === "mind");
|
|
1813
2115
|
const participantNames = participants.map((p) => p.username);
|
|
1814
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-
|
|
1815
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
2116
|
+
const { getMindManager: getMindManager2 } = await import("./mind-manager-NBJF5D26.js");
|
|
2117
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
|
|
1816
2118
|
const manager = getMindManager2();
|
|
1817
2119
|
const sm = getSleepManagerIfReady2();
|
|
1818
2120
|
const targetMinds = mindParticipants.filter((ap) => {
|
|
@@ -2345,14 +2647,15 @@ async function send4(env, channelSlug, message, images) {
|
|
|
2345
2647
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
2346
2648
|
const voluteSession = env.VOLUTE_SESSION || (env.VOLUTE_MIND_DIR ? readSessionFile(env.VOLUTE_MIND_DIR) : void 0);
|
|
2347
2649
|
if (voluteSession) headers["X-Volute-Session"] = voluteSession;
|
|
2348
|
-
const res = await fetch(`${url}/api/
|
|
2650
|
+
const res = await fetch(`${url}/api/v1/chat`, {
|
|
2349
2651
|
method: "POST",
|
|
2350
2652
|
headers,
|
|
2351
2653
|
body: JSON.stringify({
|
|
2352
2654
|
message,
|
|
2353
2655
|
conversationId,
|
|
2354
2656
|
sender: env.VOLUTE_SENDER ?? mindName,
|
|
2355
|
-
images
|
|
2657
|
+
images,
|
|
2658
|
+
targetMind: mindName
|
|
2356
2659
|
})
|
|
2357
2660
|
});
|
|
2358
2661
|
if (!res.ok) {
|
|
@@ -2624,7 +2927,7 @@ async function notifyMind(mindName, message) {
|
|
|
2624
2927
|
const entry = await findMind(mindName);
|
|
2625
2928
|
if (!entry) return;
|
|
2626
2929
|
try {
|
|
2627
|
-
const { sendSystemMessage } = await import("./system-chat-
|
|
2930
|
+
const { sendSystemMessage } = await import("./system-chat-JAPOJ3KE.js");
|
|
2628
2931
|
await sendSystemMessage(mindName, message);
|
|
2629
2932
|
} catch (err) {
|
|
2630
2933
|
logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
|
|
@@ -2644,13 +2947,13 @@ var app8 = new Hono9().post("/:name/files/send", requireSelf(), async (c) => {
|
|
|
2644
2947
|
if (pathErr) return c.json({ error: pathErr }, 400);
|
|
2645
2948
|
const senderDir = mindDir(senderName);
|
|
2646
2949
|
const filePath = resolve6(senderDir, "home", body.filePath);
|
|
2647
|
-
const
|
|
2950
|
+
const MAX_FILE_SIZE3 = 50 * 1024 * 1024;
|
|
2648
2951
|
const stat4 = statSync(filePath, { throwIfNoEntry: false });
|
|
2649
2952
|
if (!stat4) return c.json({ error: `File not found: ${body.filePath}` }, 404);
|
|
2650
|
-
if (stat4.size >
|
|
2953
|
+
if (stat4.size > MAX_FILE_SIZE3) {
|
|
2651
2954
|
return c.json(
|
|
2652
2955
|
{
|
|
2653
|
-
error: `File too large (${formatFileSize(stat4.size)}, max ${formatFileSize(
|
|
2956
|
+
error: `File too large (${formatFileSize(stat4.size)}, max ${formatFileSize(MAX_FILE_SIZE3)})`
|
|
2654
2957
|
},
|
|
2655
2958
|
413
|
|
2656
2959
|
);
|
|
@@ -2727,11 +3030,11 @@ var app8 = new Hono9().post("/:name/files/send", requireSelf(), async (c) => {
|
|
|
2727
3030
|
const pathErr = validateFilePath(body.filename);
|
|
2728
3031
|
if (pathErr) return c.json({ error: pathErr }, 400);
|
|
2729
3032
|
const content = Buffer.from(body.data, "base64");
|
|
2730
|
-
const
|
|
2731
|
-
if (content.length >
|
|
3033
|
+
const MAX_FILE_SIZE3 = 50 * 1024 * 1024;
|
|
3034
|
+
if (content.length > MAX_FILE_SIZE3) {
|
|
2732
3035
|
return c.json(
|
|
2733
3036
|
{
|
|
2734
|
-
error: `File too large (${formatFileSize(content.length)}, max ${formatFileSize(
|
|
3037
|
+
error: `File too large (${formatFileSize(content.length)}, max ${formatFileSize(MAX_FILE_SIZE3)})`
|
|
2735
3038
|
},
|
|
2736
3039
|
413
|
|
2737
3040
|
);
|
|
@@ -2796,92 +3099,301 @@ var app9 = new Hono10().get("/:name/avatar", async (c) => {
|
|
|
2796
3099
|
});
|
|
2797
3100
|
var files_default = app9;
|
|
2798
3101
|
|
|
2799
|
-
// src/web/api/
|
|
3102
|
+
// src/web/api/history.ts
|
|
3103
|
+
import { and as and3, desc as desc2, eq as eq4, inArray, sql } from "drizzle-orm";
|
|
2800
3104
|
import { Hono as Hono11 } from "hono";
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
const
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
const
|
|
2814
|
-
const
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
config2.identity = {
|
|
2819
|
-
privateKey: ".mind/identity/private.pem",
|
|
2820
|
-
publicKey: ".mind/identity/public.pem"
|
|
2821
|
-
};
|
|
2822
|
-
writeVoluteConfig(mindDir2, config2);
|
|
2823
|
-
return { publicKeyPem: publicKey, privateKeyPem: privateKey };
|
|
2824
|
-
}
|
|
2825
|
-
function getPublicKey(mindDir2) {
|
|
2826
|
-
const config2 = readVoluteConfig(mindDir2);
|
|
2827
|
-
const relPath = config2?.identity?.publicKey;
|
|
2828
|
-
if (!relPath) return null;
|
|
2829
|
-
const fullPath = resolve8(mindDir2, relPath);
|
|
2830
|
-
if (!existsSync6(fullPath)) return null;
|
|
2831
|
-
return readFileSync7(fullPath, "utf-8");
|
|
2832
|
-
}
|
|
2833
|
-
function getFingerprint(publicKeyPem) {
|
|
2834
|
-
return createHash("sha256").update(publicKeyPem).digest("hex");
|
|
2835
|
-
}
|
|
2836
|
-
async function publishPublicKey(mindName, publicKeyPem) {
|
|
2837
|
-
const systems = readSystemsConfig();
|
|
2838
|
-
if (!systems) return false;
|
|
2839
|
-
try {
|
|
2840
|
-
const res = await fetch(`${systems.apiUrl}/api/keys/${encodeURIComponent(mindName)}`, {
|
|
2841
|
-
method: "PUT",
|
|
2842
|
-
headers: {
|
|
2843
|
-
"Content-Type": "application/json",
|
|
2844
|
-
Authorization: `Bearer ${systems.apiKey}`
|
|
2845
|
-
},
|
|
2846
|
-
body: JSON.stringify({ publicKey: publicKeyPem })
|
|
2847
|
-
});
|
|
2848
|
-
if (!res.ok) {
|
|
2849
|
-
logger_default.warn(`failed to publish key for ${mindName}: ${res.status}`);
|
|
2850
|
-
return false;
|
|
2851
|
-
}
|
|
2852
|
-
return true;
|
|
2853
|
-
} catch (err) {
|
|
2854
|
-
logger_default.warn(`failed to publish key for ${mindName}`, logger_default.errorData(err));
|
|
2855
|
-
return false;
|
|
3105
|
+
var history = new Hono11().get("/turns", async (c) => {
|
|
3106
|
+
const mindFilter = c.req.query("mind");
|
|
3107
|
+
const turnIdFilter = c.req.query("turnId");
|
|
3108
|
+
const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
|
|
3109
|
+
const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
|
|
3110
|
+
const db = await getDb();
|
|
3111
|
+
const conditions = [];
|
|
3112
|
+
if (mindFilter) conditions.push(eq4(turns.mind, mindFilter));
|
|
3113
|
+
if (turnIdFilter) conditions.push(eq4(turns.id, turnIdFilter));
|
|
3114
|
+
const turnRows = await db.select().from(turns).where(conditions.length > 0 ? and3(...conditions) : void 0).orderBy(desc2(turns.created_at)).limit(limit).offset(offset);
|
|
3115
|
+
if (turnRows.length === 0) return c.json([]);
|
|
3116
|
+
const turnIds = turnRows.map((t) => t.id);
|
|
3117
|
+
const summaryRows = await db.select().from(mindHistory).where(and3(eq4(mindHistory.type, "summary"), inArray(mindHistory.turn_id, turnIds)));
|
|
3118
|
+
const summaryByTurn = /* @__PURE__ */ new Map();
|
|
3119
|
+
for (const s of summaryRows) {
|
|
3120
|
+
if (s.turn_id)
|
|
3121
|
+
summaryByTurn.set(s.turn_id, { content: s.content ?? "", metadata: s.metadata });
|
|
2856
3122
|
}
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
3123
|
+
const historyMsgRows = await db.select({
|
|
3124
|
+
id: mindHistory.id,
|
|
3125
|
+
mind: mindHistory.mind,
|
|
3126
|
+
type: mindHistory.type,
|
|
3127
|
+
channel: mindHistory.channel,
|
|
3128
|
+
sender: mindHistory.sender,
|
|
3129
|
+
content: mindHistory.content,
|
|
3130
|
+
turn_id: mindHistory.turn_id,
|
|
3131
|
+
created_at: mindHistory.created_at
|
|
3132
|
+
}).from(mindHistory).where(
|
|
3133
|
+
and3(
|
|
3134
|
+
inArray(mindHistory.turn_id, turnIds),
|
|
3135
|
+
sql`${mindHistory.type} IN ('inbound', 'outbound')`
|
|
3136
|
+
)
|
|
3137
|
+
).orderBy(mindHistory.created_at);
|
|
3138
|
+
const msgsByTurnChannel = /* @__PURE__ */ new Map();
|
|
3139
|
+
function addToTurnChannel(turnId, channel, event) {
|
|
3140
|
+
let byChannel = msgsByTurnChannel.get(turnId);
|
|
3141
|
+
if (!byChannel) {
|
|
3142
|
+
byChannel = /* @__PURE__ */ new Map();
|
|
3143
|
+
msgsByTurnChannel.set(turnId, byChannel);
|
|
3144
|
+
}
|
|
3145
|
+
let arr = byChannel.get(channel);
|
|
3146
|
+
if (!arr) {
|
|
3147
|
+
arr = [];
|
|
3148
|
+
byChannel.set(channel, arr);
|
|
2870
3149
|
}
|
|
3150
|
+
arr.push(event);
|
|
2871
3151
|
}
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
3152
|
+
const turnMindMap = /* @__PURE__ */ new Map();
|
|
3153
|
+
for (const t of turnRows) turnMindMap.set(t.id, t.mind);
|
|
3154
|
+
for (const m of historyMsgRows) {
|
|
3155
|
+
if (!m.turn_id || !m.channel) continue;
|
|
3156
|
+
const mindName = turnMindMap.get(m.turn_id) ?? m.mind;
|
|
3157
|
+
addToTurnChannel(m.turn_id, m.channel, {
|
|
3158
|
+
id: m.id,
|
|
3159
|
+
role: m.type === "inbound" ? "user" : "assistant",
|
|
3160
|
+
sender: m.type === "inbound" ? m.sender ?? null : mindName,
|
|
3161
|
+
content: m.content,
|
|
3162
|
+
created_at: m.created_at
|
|
3163
|
+
});
|
|
3164
|
+
}
|
|
3165
|
+
function getChannelLabel(channel) {
|
|
3166
|
+
const isDM = channel.startsWith("@");
|
|
3167
|
+
const colonIdx = channel.indexOf(":");
|
|
3168
|
+
const raw = colonIdx >= 0 ? channel.substring(colonIdx + 1) : channel;
|
|
3169
|
+
const label = isDM ? raw : raw.startsWith("#") ? raw : `#${raw}`;
|
|
3170
|
+
return { label, type: isDM ? "dm" : "channel" };
|
|
3171
|
+
}
|
|
3172
|
+
const activityRows = await db.select().from(activity).where(inArray(activity.turn_id, turnIds)).orderBy(activity.created_at);
|
|
3173
|
+
const activitiesByTurn = /* @__PURE__ */ new Map();
|
|
3174
|
+
for (const a of activityRows) {
|
|
3175
|
+
if (!a.turn_id) continue;
|
|
3176
|
+
let arr = activitiesByTurn.get(a.turn_id);
|
|
3177
|
+
if (!arr) {
|
|
3178
|
+
arr = [];
|
|
3179
|
+
activitiesByTurn.set(a.turn_id, arr);
|
|
3180
|
+
}
|
|
3181
|
+
arr.push(a);
|
|
3182
|
+
}
|
|
3183
|
+
const triggerIds = turnRows.filter((t) => t.trigger_event_id != null).map((t) => t.trigger_event_id);
|
|
3184
|
+
const triggerMap = /* @__PURE__ */ new Map();
|
|
3185
|
+
if (triggerIds.length > 0) {
|
|
3186
|
+
const triggerRows = await db.select({
|
|
3187
|
+
id: mindHistory.id,
|
|
3188
|
+
channel: mindHistory.channel,
|
|
3189
|
+
sender: mindHistory.sender,
|
|
3190
|
+
content: mindHistory.content
|
|
3191
|
+
}).from(mindHistory).where(inArray(mindHistory.id, triggerIds));
|
|
3192
|
+
for (const r of triggerRows) triggerMap.set(r.id, r);
|
|
3193
|
+
}
|
|
3194
|
+
const result = turnRows.map((t) => {
|
|
3195
|
+
const summary = summaryByTurn.get(t.id);
|
|
3196
|
+
const turnChannels = msgsByTurnChannel.get(t.id) ?? /* @__PURE__ */ new Map();
|
|
3197
|
+
const convEntries = [...turnChannels.entries()].map(([channel, evts]) => {
|
|
3198
|
+
const { label, type } = getChannelLabel(channel);
|
|
3199
|
+
return {
|
|
3200
|
+
id: channel,
|
|
3201
|
+
label,
|
|
3202
|
+
type,
|
|
3203
|
+
messages: evts.map((m) => ({
|
|
3204
|
+
id: m.id,
|
|
3205
|
+
role: m.role,
|
|
3206
|
+
sender_name: m.sender,
|
|
3207
|
+
content: [{ type: "text", text: m.content ?? "" }],
|
|
3208
|
+
source_event_id: m.id,
|
|
3209
|
+
created_at: m.created_at
|
|
3210
|
+
}))
|
|
3211
|
+
};
|
|
3212
|
+
});
|
|
3213
|
+
const turnActivities = (activitiesByTurn.get(t.id) ?? []).map((a) => {
|
|
3214
|
+
let metadata = null;
|
|
3215
|
+
if (a.metadata) {
|
|
3216
|
+
try {
|
|
3217
|
+
metadata = JSON.parse(a.metadata);
|
|
3218
|
+
} catch (err) {
|
|
3219
|
+
logger_default.debug(`malformed activity metadata for activity ${a.id}`, logger_default.errorData(err));
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
return {
|
|
3223
|
+
id: a.id,
|
|
3224
|
+
type: a.type,
|
|
3225
|
+
summary: a.summary,
|
|
3226
|
+
metadata,
|
|
3227
|
+
source_event_id: a.source_event_id,
|
|
3228
|
+
created_at: a.created_at
|
|
3229
|
+
};
|
|
3230
|
+
});
|
|
3231
|
+
let summaryMeta = null;
|
|
3232
|
+
if (summary?.metadata) {
|
|
3233
|
+
try {
|
|
3234
|
+
summaryMeta = JSON.parse(summary.metadata);
|
|
3235
|
+
} catch (err) {
|
|
3236
|
+
logger_default.debug(`malformed summary metadata for turn ${t.id}`, logger_default.errorData(err));
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
const trigger = t.trigger_event_id ? triggerMap.get(t.trigger_event_id) : null;
|
|
3240
|
+
return {
|
|
3241
|
+
id: t.id,
|
|
3242
|
+
mind: t.mind,
|
|
3243
|
+
summary: summary?.content ?? null,
|
|
3244
|
+
summary_meta: summaryMeta,
|
|
3245
|
+
status: t.status,
|
|
3246
|
+
created_at: t.created_at,
|
|
3247
|
+
trigger: trigger ? { channel: trigger.channel, sender: trigger.sender, content: trigger.content } : null,
|
|
3248
|
+
conversations: convEntries,
|
|
3249
|
+
activities: turnActivities
|
|
3250
|
+
};
|
|
3251
|
+
});
|
|
3252
|
+
return c.json(result);
|
|
3253
|
+
}).get("/events", async (c) => {
|
|
3254
|
+
const mindFilter = c.req.query("mind");
|
|
3255
|
+
const stream = new ReadableStream({
|
|
3256
|
+
start(controller) {
|
|
3257
|
+
const encoder = new TextEncoder();
|
|
3258
|
+
const send5 = (data) => {
|
|
3259
|
+
controller.enqueue(encoder.encode(`data: ${data}
|
|
3260
|
+
|
|
3261
|
+
`));
|
|
3262
|
+
};
|
|
3263
|
+
let unsubscribe;
|
|
3264
|
+
const pingInterval = setInterval(() => {
|
|
3265
|
+
try {
|
|
3266
|
+
controller.enqueue(encoder.encode(": ping\n\n"));
|
|
3267
|
+
} catch {
|
|
3268
|
+
clearInterval(pingInterval);
|
|
3269
|
+
unsubscribe?.();
|
|
3270
|
+
}
|
|
3271
|
+
}, 15e3);
|
|
3272
|
+
if (mindFilter) {
|
|
3273
|
+
unsubscribe = subscribe3(mindFilter, (event) => {
|
|
3274
|
+
try {
|
|
3275
|
+
send5(JSON.stringify(event));
|
|
3276
|
+
} catch {
|
|
3277
|
+
clearInterval(pingInterval);
|
|
3278
|
+
unsubscribe?.();
|
|
3279
|
+
}
|
|
3280
|
+
});
|
|
3281
|
+
} else {
|
|
3282
|
+
unsubscribe = subscribeAll((event) => {
|
|
3283
|
+
try {
|
|
3284
|
+
send5(JSON.stringify(event));
|
|
3285
|
+
} catch {
|
|
3286
|
+
clearInterval(pingInterval);
|
|
3287
|
+
unsubscribe?.();
|
|
3288
|
+
}
|
|
3289
|
+
});
|
|
3290
|
+
}
|
|
3291
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
3292
|
+
clearInterval(pingInterval);
|
|
3293
|
+
unsubscribe?.();
|
|
3294
|
+
try {
|
|
3295
|
+
controller.close();
|
|
3296
|
+
} catch {
|
|
3297
|
+
}
|
|
3298
|
+
});
|
|
3299
|
+
}
|
|
3300
|
+
});
|
|
3301
|
+
return new Response(stream, {
|
|
3302
|
+
headers: {
|
|
3303
|
+
"Content-Type": "text/event-stream",
|
|
3304
|
+
"Cache-Control": "no-cache",
|
|
3305
|
+
Connection: "keep-alive"
|
|
3306
|
+
}
|
|
3307
|
+
});
|
|
3308
|
+
});
|
|
3309
|
+
var history_default = history;
|
|
3310
|
+
|
|
3311
|
+
// src/web/api/keys.ts
|
|
3312
|
+
import { Hono as Hono12 } from "hono";
|
|
3313
|
+
|
|
3314
|
+
// src/lib/identity.ts
|
|
3315
|
+
import { createHash, generateKeyPairSync, sign, verify } from "crypto";
|
|
3316
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
3317
|
+
import { resolve as resolve8 } from "path";
|
|
3318
|
+
function generateIdentity(mindDir2) {
|
|
3319
|
+
const identityDir = resolve8(mindDir2, ".mind/identity");
|
|
3320
|
+
mkdirSync4(identityDir, { recursive: true });
|
|
3321
|
+
const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
|
|
3322
|
+
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
3323
|
+
privateKeyEncoding: { type: "pkcs8", format: "pem" }
|
|
3324
|
+
});
|
|
3325
|
+
const privatePath = resolve8(identityDir, "private.pem");
|
|
3326
|
+
const publicPath = resolve8(identityDir, "public.pem");
|
|
3327
|
+
writeFileSync4(privatePath, privateKey, { mode: 384 });
|
|
3328
|
+
writeFileSync4(publicPath, publicKey, { mode: 420 });
|
|
3329
|
+
const config2 = readVoluteConfig(mindDir2) ?? {};
|
|
3330
|
+
config2.identity = {
|
|
3331
|
+
privateKey: ".mind/identity/private.pem",
|
|
3332
|
+
publicKey: ".mind/identity/public.pem"
|
|
3333
|
+
};
|
|
3334
|
+
writeVoluteConfig(mindDir2, config2);
|
|
3335
|
+
return { publicKeyPem: publicKey, privateKeyPem: privateKey };
|
|
3336
|
+
}
|
|
3337
|
+
function getPublicKey(mindDir2) {
|
|
3338
|
+
const config2 = readVoluteConfig(mindDir2);
|
|
3339
|
+
const relPath = config2?.identity?.publicKey;
|
|
3340
|
+
if (!relPath) return null;
|
|
3341
|
+
const fullPath = resolve8(mindDir2, relPath);
|
|
3342
|
+
if (!existsSync6(fullPath)) return null;
|
|
3343
|
+
return readFileSync7(fullPath, "utf-8");
|
|
3344
|
+
}
|
|
3345
|
+
function getFingerprint(publicKeyPem) {
|
|
3346
|
+
return createHash("sha256").update(publicKeyPem).digest("hex");
|
|
3347
|
+
}
|
|
3348
|
+
async function publishPublicKey(mindName, publicKeyPem) {
|
|
3349
|
+
const systems = readSystemsConfig();
|
|
3350
|
+
if (!systems) return false;
|
|
3351
|
+
try {
|
|
3352
|
+
const res = await fetch(`${systems.apiUrl}/api/keys/${encodeURIComponent(mindName)}`, {
|
|
3353
|
+
method: "PUT",
|
|
3354
|
+
headers: {
|
|
3355
|
+
"Content-Type": "application/json",
|
|
3356
|
+
Authorization: `Bearer ${systems.apiKey}`
|
|
3357
|
+
},
|
|
3358
|
+
body: JSON.stringify({ publicKey: publicKeyPem })
|
|
3359
|
+
});
|
|
3360
|
+
if (!res.ok) {
|
|
3361
|
+
logger_default.warn(`failed to publish key for ${mindName}: ${res.status}`);
|
|
3362
|
+
return false;
|
|
3363
|
+
}
|
|
3364
|
+
return true;
|
|
3365
|
+
} catch (err) {
|
|
3366
|
+
logger_default.warn(`failed to publish key for ${mindName}`, logger_default.errorData(err));
|
|
3367
|
+
return false;
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
|
|
3371
|
+
// src/web/api/keys.ts
|
|
3372
|
+
var app10 = new Hono12().get("/:fingerprint", async (c) => {
|
|
3373
|
+
const fingerprint = c.req.param("fingerprint");
|
|
3374
|
+
for (const entry of await readRegistry()) {
|
|
3375
|
+
try {
|
|
3376
|
+
const pubKey = getPublicKey(mindDir(entry.name));
|
|
3377
|
+
if (!pubKey) continue;
|
|
3378
|
+
if (getFingerprint(pubKey) === fingerprint) {
|
|
3379
|
+
return c.json({ publicKey: pubKey, mind: entry.name });
|
|
3380
|
+
}
|
|
3381
|
+
} catch {
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
return c.json({ error: "Key not found" }, 404);
|
|
3385
|
+
});
|
|
3386
|
+
var keys_default = app10;
|
|
3387
|
+
|
|
3388
|
+
// src/web/api/logs.ts
|
|
3389
|
+
import { spawn as spawn2 } from "child_process";
|
|
3390
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3391
|
+
import { resolve as resolve9 } from "path";
|
|
3392
|
+
import { Hono as Hono13 } from "hono";
|
|
3393
|
+
import { streamSSE as streamSSE3 } from "hono/streaming";
|
|
3394
|
+
var app11 = new Hono13().get("/:name/logs", async (c) => {
|
|
3395
|
+
const name = c.req.param("name");
|
|
3396
|
+
const entry = await findMind(name);
|
|
2885
3397
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
2886
3398
|
const logFile = resolve9(stateDir(name), "logs", "mind.log");
|
|
2887
3399
|
if (!existsSync7(logFile)) {
|
|
@@ -2931,9 +3443,9 @@ var logs_default = app11;
|
|
|
2931
3443
|
|
|
2932
3444
|
// src/web/api/mind-skills.ts
|
|
2933
3445
|
import { zValidator as zValidator4 } from "@hono/zod-validator";
|
|
2934
|
-
import { Hono as
|
|
3446
|
+
import { Hono as Hono14 } from "hono";
|
|
2935
3447
|
import { z as z4 } from "zod";
|
|
2936
|
-
var app12 = new
|
|
3448
|
+
var app12 = new Hono14().get("/:name/skills", async (c) => {
|
|
2937
3449
|
const name = c.req.param("name");
|
|
2938
3450
|
const entry = await findMind(name);
|
|
2939
3451
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -3022,8 +3534,8 @@ import {
|
|
|
3022
3534
|
} from "fs";
|
|
3023
3535
|
import { resolve as resolve12 } from "path";
|
|
3024
3536
|
import { zValidator as zValidator5 } from "@hono/zod-validator";
|
|
3025
|
-
import { and as
|
|
3026
|
-
import { Hono as
|
|
3537
|
+
import { and as and5, desc as desc4, eq as eq6, sql as sql3 } from "drizzle-orm";
|
|
3538
|
+
import { Hono as Hono15 } from "hono";
|
|
3027
3539
|
import { z as z5 } from "zod";
|
|
3028
3540
|
|
|
3029
3541
|
// src/lib/consolidate.ts
|
|
@@ -3272,7 +3784,7 @@ function convertAssistantContent(content) {
|
|
|
3272
3784
|
}
|
|
3273
3785
|
|
|
3274
3786
|
// src/lib/daemon/turn-summarizer.ts
|
|
3275
|
-
import { and as
|
|
3787
|
+
import { and as and4, desc as desc3, eq as eq5, gt, lt as lt3, sql as sql2 } from "drizzle-orm";
|
|
3276
3788
|
|
|
3277
3789
|
// src/lib/format-tool.ts
|
|
3278
3790
|
function summarizeTool(name, input) {
|
|
@@ -3292,22 +3804,22 @@ var sLog = logger_default.child("turn-summarizer");
|
|
|
3292
3804
|
async function gatherTurnEvents(mind, session, doneId) {
|
|
3293
3805
|
const db = await getDb();
|
|
3294
3806
|
const conditions = [
|
|
3295
|
-
|
|
3296
|
-
|
|
3807
|
+
eq5(mindHistory.mind, mind),
|
|
3808
|
+
eq5(mindHistory.type, "done"),
|
|
3297
3809
|
lt3(mindHistory.id, doneId)
|
|
3298
3810
|
];
|
|
3299
3811
|
if (session) {
|
|
3300
|
-
conditions.push(
|
|
3812
|
+
conditions.push(eq5(mindHistory.session, session));
|
|
3301
3813
|
}
|
|
3302
|
-
const prevDone = await db.select({ id: mindHistory.id }).from(mindHistory).where(
|
|
3814
|
+
const prevDone = await db.select({ id: mindHistory.id }).from(mindHistory).where(and4(...conditions)).orderBy(desc3(mindHistory.id)).limit(1);
|
|
3303
3815
|
const prevDoneId = prevDone.length > 0 ? prevDone[0].id : 0;
|
|
3304
3816
|
const turnConditions = [
|
|
3305
|
-
|
|
3817
|
+
eq5(mindHistory.mind, mind),
|
|
3306
3818
|
gt(mindHistory.id, prevDoneId),
|
|
3307
|
-
|
|
3819
|
+
sql2`${mindHistory.id} <= ${doneId}`
|
|
3308
3820
|
];
|
|
3309
3821
|
if (session) {
|
|
3310
|
-
turnConditions.push(
|
|
3822
|
+
turnConditions.push(eq5(mindHistory.session, session));
|
|
3311
3823
|
}
|
|
3312
3824
|
const events = await db.select({
|
|
3313
3825
|
id: mindHistory.id,
|
|
@@ -3317,7 +3829,7 @@ async function gatherTurnEvents(mind, session, doneId) {
|
|
|
3317
3829
|
content: mindHistory.content,
|
|
3318
3830
|
metadata: mindHistory.metadata,
|
|
3319
3831
|
created_at: mindHistory.created_at
|
|
3320
|
-
}).from(mindHistory).where(
|
|
3832
|
+
}).from(mindHistory).where(and4(...turnConditions)).orderBy(mindHistory.id);
|
|
3321
3833
|
return {
|
|
3322
3834
|
events,
|
|
3323
3835
|
fromId: events.length > 0 ? events[0].id : doneId,
|
|
@@ -3420,7 +3932,7 @@ async function gatherTurnEventsByTurnId(turnId) {
|
|
|
3420
3932
|
content: mindHistory.content,
|
|
3421
3933
|
metadata: mindHistory.metadata,
|
|
3422
3934
|
created_at: mindHistory.created_at
|
|
3423
|
-
}).from(mindHistory).where(
|
|
3935
|
+
}).from(mindHistory).where(eq5(mindHistory.turn_id, turnId)).orderBy(mindHistory.id);
|
|
3424
3936
|
return {
|
|
3425
3937
|
events,
|
|
3426
3938
|
fromId: events.length > 0 ? events[0].id : 0,
|
|
@@ -3439,8 +3951,8 @@ async function summarizeTurn(mind, session, channel, doneId, turnId) {
|
|
|
3439
3951
|
if (turnId) {
|
|
3440
3952
|
try {
|
|
3441
3953
|
const db2 = await getDb();
|
|
3442
|
-
await db2.update(mindHistory).set({ turn_id: null }).where(
|
|
3443
|
-
await db2.update(messages).set({ turn_id: null }).where(
|
|
3954
|
+
await db2.update(mindHistory).set({ turn_id: null }).where(and4(eq5(mindHistory.turn_id, turnId), eq5(mindHistory.type, "inbound")));
|
|
3955
|
+
await db2.update(messages).set({ turn_id: null }).where(eq5(messages.turn_id, turnId));
|
|
3444
3956
|
} catch (err) {
|
|
3445
3957
|
sLog.error(`failed to un-tag events for interrupted turn ${turnId}`, logger_default.errorData(err));
|
|
3446
3958
|
}
|
|
@@ -3600,7 +4112,7 @@ async function getMindStatus(name, port) {
|
|
|
3600
4112
|
const manager = getMindManager();
|
|
3601
4113
|
let status = "stopped";
|
|
3602
4114
|
try {
|
|
3603
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4115
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
|
|
3604
4116
|
if (getSleepManagerIfReady2()?.isSleeping(name)) {
|
|
3605
4117
|
status = "sleeping";
|
|
3606
4118
|
}
|
|
@@ -4027,7 +4539,7 @@ function formatTimeAgo(date) {
|
|
|
4027
4539
|
const days = Math.floor(hours / 24);
|
|
4028
4540
|
return `${days}d ago`;
|
|
4029
4541
|
}
|
|
4030
|
-
var app13 = new
|
|
4542
|
+
var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", createMindSchema), async (c) => {
|
|
4031
4543
|
const body = c.req.valid("json");
|
|
4032
4544
|
const { name } = body;
|
|
4033
4545
|
const template = body.template ?? resolveTemplate(body.model);
|
|
@@ -4119,7 +4631,13 @@ The human who planted you described you as: "${body.description}"
|
|
|
4119
4631
|
const seedSoul = body.seedSoul ? substitute(seedSoulRaw, { name, description: descLine }) : seedSoulRaw;
|
|
4120
4632
|
writeFileSync7(resolve12(dest, "home/SOUL.md"), seedSoul);
|
|
4121
4633
|
}
|
|
4122
|
-
|
|
4634
|
+
let skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
|
|
4635
|
+
if (body.stage === "seed" && !body.skills) {
|
|
4636
|
+
const { isImagegenEnabled } = await import("./setup-TISPCO22.js");
|
|
4637
|
+
if (isImagegenEnabled()) {
|
|
4638
|
+
skillSet = [...skillSet, "imagegen"];
|
|
4639
|
+
}
|
|
4640
|
+
}
|
|
4123
4641
|
const skillWarnings = [];
|
|
4124
4642
|
for (const skillId of skillSet) {
|
|
4125
4643
|
try {
|
|
@@ -4129,6 +4647,33 @@ The human who planted you described you as: "${body.description}"
|
|
|
4129
4647
|
skillWarnings.push(`Failed to install skill: ${skillId}`);
|
|
4130
4648
|
}
|
|
4131
4649
|
}
|
|
4650
|
+
if (body.stage === "seed") {
|
|
4651
|
+
try {
|
|
4652
|
+
const spiritEntry = await findMind("volute");
|
|
4653
|
+
if (spiritEntry) {
|
|
4654
|
+
const { spiritDir } = await import("./spirit-VRONKFMF.js");
|
|
4655
|
+
const sDir = spiritEntry.dir ?? spiritDir();
|
|
4656
|
+
const spiritConfig = readVoluteConfig(sDir) ?? {};
|
|
4657
|
+
const schedules = spiritConfig.schedules ?? [];
|
|
4658
|
+
const nurtureId = `nurture-${name}`;
|
|
4659
|
+
if (!schedules.some((s) => s.id === nurtureId)) {
|
|
4660
|
+
schedules.push({
|
|
4661
|
+
id: nurtureId,
|
|
4662
|
+
cron: process.env.VOLUTE_NURTURE_CRON ?? "*/5 * * * *",
|
|
4663
|
+
script: `volute seed check ${name}`,
|
|
4664
|
+
enabled: true,
|
|
4665
|
+
whileSleeping: "skip"
|
|
4666
|
+
});
|
|
4667
|
+
spiritConfig.schedules = schedules;
|
|
4668
|
+
writeVoluteConfig(sDir, spiritConfig);
|
|
4669
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-ZZ7XGQG6.js");
|
|
4670
|
+
getScheduler2().loadSchedules("volute", sDir);
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
4673
|
+
} catch (err) {
|
|
4674
|
+
logger_default.warn(`failed to add nurture schedule for ${name}`, logger_default.errorData(err));
|
|
4675
|
+
}
|
|
4676
|
+
}
|
|
4132
4677
|
if (body.stage !== "seed") {
|
|
4133
4678
|
const customSoul = await getPromptIfCustom("default_soul");
|
|
4134
4679
|
if (customSoul) {
|
|
@@ -4300,7 +4845,7 @@ ${user.trimEnd()}
|
|
|
4300
4845
|
const db = await getDb();
|
|
4301
4846
|
const lastActiveRows = await db.select({
|
|
4302
4847
|
mind: mindHistory.mind,
|
|
4303
|
-
lastActiveAt:
|
|
4848
|
+
lastActiveAt: sql3`MAX(${mindHistory.created_at})`
|
|
4304
4849
|
}).from(mindHistory).groupBy(mindHistory.mind);
|
|
4305
4850
|
lastActiveMap = new Map(lastActiveRows.map((r) => [r.mind, r.lastActiveAt]));
|
|
4306
4851
|
} catch {
|
|
@@ -4384,7 +4929,7 @@ ${user.trimEnd()}
|
|
|
4384
4929
|
const manager = getMindManager();
|
|
4385
4930
|
try {
|
|
4386
4931
|
if (context?.type === "reload") {
|
|
4387
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4932
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
|
|
4388
4933
|
const sleepState = getSleepManagerIfReady2()?.getState(name);
|
|
4389
4934
|
if (sleepState?.sleeping) {
|
|
4390
4935
|
logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
|
|
@@ -4446,7 +4991,7 @@ ${user.trimEnd()}
|
|
|
4446
4991
|
if (context?.type === "sprouted" && !entry.parent) {
|
|
4447
4992
|
try {
|
|
4448
4993
|
const db = await getDb();
|
|
4449
|
-
const activeConvs = await db.select({ id: conversations.id, channel: conversations.channel }).from(conversations).where(
|
|
4994
|
+
const activeConvs = await db.select({ id: conversations.id, channel: conversations.channel }).from(conversations).where(eq6(conversations.mind_name, baseName)).all();
|
|
4450
4995
|
for (const conv of activeConvs) {
|
|
4451
4996
|
await recordInbound(baseName, conv.channel, "system", "[seed has sprouted]");
|
|
4452
4997
|
await addMessage(conv.id, "assistant", "system", [
|
|
@@ -4480,7 +5025,7 @@ ${user.trimEnd()}
|
|
|
4480
5025
|
const name = c.req.param("name");
|
|
4481
5026
|
const entry = await findMind(name);
|
|
4482
5027
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4483
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5028
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
|
|
4484
5029
|
const sm = getSleepManagerIfReady2();
|
|
4485
5030
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4486
5031
|
return c.json(sm.getState(name));
|
|
@@ -4488,7 +5033,7 @@ ${user.trimEnd()}
|
|
|
4488
5033
|
const name = c.req.param("name");
|
|
4489
5034
|
const entry = await findMind(name);
|
|
4490
5035
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4491
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5036
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
|
|
4492
5037
|
const sm = getSleepManagerIfReady2();
|
|
4493
5038
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4494
5039
|
if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
|
|
@@ -4508,7 +5053,7 @@ ${user.trimEnd()}
|
|
|
4508
5053
|
const name = c.req.param("name");
|
|
4509
5054
|
const entry = await findMind(name);
|
|
4510
5055
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4511
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5056
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
|
|
4512
5057
|
const sm = getSleepManagerIfReady2();
|
|
4513
5058
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4514
5059
|
const sleepState = sm.getState(name);
|
|
@@ -4523,11 +5068,98 @@ ${user.trimEnd()}
|
|
|
4523
5068
|
const name = c.req.param("name");
|
|
4524
5069
|
const entry = await findMind(name);
|
|
4525
5070
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4526
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5071
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
|
|
4527
5072
|
const sm = getSleepManagerIfReady2();
|
|
4528
5073
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4529
5074
|
const flushed = await sm.flushQueuedMessages(name);
|
|
4530
5075
|
return c.json({ ok: true, flushed });
|
|
5076
|
+
}).patch("/:name/profile", requireSelf(), async (c) => {
|
|
5077
|
+
const name = c.req.param("name");
|
|
5078
|
+
const entry = await findMind(name);
|
|
5079
|
+
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5080
|
+
const body = await c.req.json();
|
|
5081
|
+
const dir = entry.dir ?? mindDir(name);
|
|
5082
|
+
const config2 = readVoluteConfig(dir) ?? {};
|
|
5083
|
+
const profile = config2.profile ?? {};
|
|
5084
|
+
if (body.displayName !== void 0) profile.displayName = body.displayName;
|
|
5085
|
+
if (body.description !== void 0) profile.description = body.description;
|
|
5086
|
+
if (body.avatar !== void 0) profile.avatar = body.avatar;
|
|
5087
|
+
config2.profile = profile;
|
|
5088
|
+
writeVoluteConfig(dir, config2);
|
|
5089
|
+
const { syncMindProfile } = await import("./auth-GKCDSO4T.js");
|
|
5090
|
+
await syncMindProfile(name, profile);
|
|
5091
|
+
broadcast({ type: "profile_updated", mind: name, summary: `${name} profile updated` });
|
|
5092
|
+
return c.json({ ok: true });
|
|
5093
|
+
}).get("/:name/seed-check", requireSelf(), async (c) => {
|
|
5094
|
+
const name = c.req.param("name");
|
|
5095
|
+
const entry = await findMind(name);
|
|
5096
|
+
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5097
|
+
if (entry.stage !== "seed") return c.json({ output: "" });
|
|
5098
|
+
const db = await getDb();
|
|
5099
|
+
const ORIENTATION_MARKER = "You don't have a soul yet";
|
|
5100
|
+
const rawCreator = Number(process.env.VOLUTE_NURTURE_CREATOR_MINUTES);
|
|
5101
|
+
const creatorThreshold = Number.isNaN(rawCreator) ? 5 : rawCreator;
|
|
5102
|
+
const rawSpirit = Number(process.env.VOLUTE_NURTURE_SPIRIT_MINUTES);
|
|
5103
|
+
const spiritThreshold = Number.isNaN(rawSpirit) ? 15 : rawSpirit;
|
|
5104
|
+
const lastCreatorMsg = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(
|
|
5105
|
+
and5(
|
|
5106
|
+
eq6(mindHistory.mind, name),
|
|
5107
|
+
eq6(mindHistory.type, "inbound"),
|
|
5108
|
+
sql3`${mindHistory.sender} != 'volute'`,
|
|
5109
|
+
sql3`${mindHistory.sender} != ${name}`,
|
|
5110
|
+
sql3`${mindHistory.sender} IS NOT NULL`
|
|
5111
|
+
)
|
|
5112
|
+
).orderBy(desc4(mindHistory.created_at)).limit(1);
|
|
5113
|
+
const lastSpiritMsg = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(
|
|
5114
|
+
and5(
|
|
5115
|
+
eq6(mindHistory.mind, name),
|
|
5116
|
+
eq6(mindHistory.type, "inbound"),
|
|
5117
|
+
eq6(mindHistory.sender, "volute")
|
|
5118
|
+
)
|
|
5119
|
+
).orderBy(desc4(mindHistory.created_at)).limit(1);
|
|
5120
|
+
const now = Date.now();
|
|
5121
|
+
const creatorTime = lastCreatorMsg[0] ? new Date(lastCreatorMsg[0].created_at).getTime() : 0;
|
|
5122
|
+
const spiritTime = lastSpiritMsg[0] ? new Date(lastSpiritMsg[0].created_at).getTime() : 0;
|
|
5123
|
+
const minutesSinceCreator = creatorTime ? (now - creatorTime) / 6e4 : Infinity;
|
|
5124
|
+
const minutesSinceSpirit = spiritTime ? (now - spiritTime) / 6e4 : Infinity;
|
|
5125
|
+
if (minutesSinceCreator < creatorThreshold && minutesSinceSpirit < spiritThreshold) {
|
|
5126
|
+
return c.json({ output: "" });
|
|
5127
|
+
}
|
|
5128
|
+
const dir = entry.dir ?? mindDir(name);
|
|
5129
|
+
const soulPath = resolve12(dir, "home/SOUL.md");
|
|
5130
|
+
const memoryPath = resolve12(dir, "home/MEMORY.md");
|
|
5131
|
+
const soulCustom = existsSync9(soulPath) && !readFileSync10(soulPath, "utf-8").includes(ORIENTATION_MARKER);
|
|
5132
|
+
const memoryWritten = existsSync9(memoryPath) && readFileSync10(memoryPath, "utf-8").trim().length > 0;
|
|
5133
|
+
const config2 = readVoluteConfig(dir);
|
|
5134
|
+
const displayNameSet = !!config2?.profile?.displayName;
|
|
5135
|
+
const avatarSet = !!config2?.profile?.avatar;
|
|
5136
|
+
const { isImagegenEnabled } = await import("./setup-TISPCO22.js");
|
|
5137
|
+
const imagegenEnabled = isImagegenEnabled();
|
|
5138
|
+
const done = [];
|
|
5139
|
+
const remaining = [];
|
|
5140
|
+
if (soulCustom) done.push("SOUL.md written");
|
|
5141
|
+
else remaining.push("Write SOUL.md");
|
|
5142
|
+
if (memoryWritten) done.push("MEMORY.md written");
|
|
5143
|
+
else remaining.push("Write MEMORY.md");
|
|
5144
|
+
if (displayNameSet) done.push("Display name set");
|
|
5145
|
+
else remaining.push("Set display name");
|
|
5146
|
+
if (imagegenEnabled) {
|
|
5147
|
+
if (avatarSet) done.push("Avatar set");
|
|
5148
|
+
else remaining.push("Generate and set avatar");
|
|
5149
|
+
}
|
|
5150
|
+
const creatorStatus = minutesSinceCreator === Infinity ? "No creator messages yet" : `Last creator message: ${Math.round(minutesSinceCreator)} minutes ago`;
|
|
5151
|
+
const lines = [`Seed: ${name}`, creatorStatus];
|
|
5152
|
+
if (done.length > 0) lines.push(`Done: ${done.join(", ")}`);
|
|
5153
|
+
if (remaining.length > 0) lines.push(`Remaining: ${remaining.join(", ")}`);
|
|
5154
|
+
if (remaining.length > 0) {
|
|
5155
|
+
lines.push("", `DM the seed to encourage them: echo "message" | volute chat send @${name}`);
|
|
5156
|
+
} else {
|
|
5157
|
+
lines.push(
|
|
5158
|
+
"",
|
|
5159
|
+
`All checklist items complete \u2014 the seed can run \`volute seed sprout\` when ready.`
|
|
5160
|
+
);
|
|
5161
|
+
}
|
|
5162
|
+
return c.json({ output: lines.join("\n") });
|
|
4531
5163
|
}).post("/:name/sprout", requireSelf(), async (c) => {
|
|
4532
5164
|
const name = c.req.param("name");
|
|
4533
5165
|
const entry = await findMind(name);
|
|
@@ -4536,6 +5168,24 @@ ${user.trimEnd()}
|
|
|
4536
5168
|
return c.json({ error: `Mind is not a seed (stage: ${entry.stage})` }, 409);
|
|
4537
5169
|
}
|
|
4538
5170
|
await setMindStage(name, "sprouted");
|
|
5171
|
+
try {
|
|
5172
|
+
const spiritEntry = await findMind("volute");
|
|
5173
|
+
if (spiritEntry) {
|
|
5174
|
+
const { spiritDir } = await import("./spirit-VRONKFMF.js");
|
|
5175
|
+
const sDir = spiritEntry.dir ?? spiritDir();
|
|
5176
|
+
const spiritConfig = readVoluteConfig(sDir);
|
|
5177
|
+
if (spiritConfig?.schedules) {
|
|
5178
|
+
const nurtureId = `nurture-${name}`;
|
|
5179
|
+
spiritConfig.schedules = spiritConfig.schedules.filter((s) => s.id !== nurtureId);
|
|
5180
|
+
if (spiritConfig.schedules.length === 0) spiritConfig.schedules = void 0;
|
|
5181
|
+
writeVoluteConfig(sDir, spiritConfig);
|
|
5182
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-ZZ7XGQG6.js");
|
|
5183
|
+
getScheduler2().loadSchedules("volute", sDir);
|
|
5184
|
+
}
|
|
5185
|
+
}
|
|
5186
|
+
} catch (err) {
|
|
5187
|
+
logger_default.warn(`failed to clean up nurture schedule for ${name}`, logger_default.errorData(err));
|
|
5188
|
+
}
|
|
4539
5189
|
return c.json({ ok: true });
|
|
4540
5190
|
}).delete("/:name", requireAdmin, async (c) => {
|
|
4541
5191
|
const name = c.req.param("name");
|
|
@@ -4953,7 +5603,7 @@ ${user.trimEnd()}
|
|
|
4953
5603
|
if (!body.systemPrompt || !body.message) {
|
|
4954
5604
|
return c.json({ error: "systemPrompt and message required" }, 400);
|
|
4955
5605
|
}
|
|
4956
|
-
const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-
|
|
5606
|
+
const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-SBY2WG7O.js");
|
|
4957
5607
|
if (!isAiConfigured()) {
|
|
4958
5608
|
return c.json({ error: "AI service not configured" }, 503);
|
|
4959
5609
|
}
|
|
@@ -5003,6 +5653,8 @@ ${user.trimEnd()}
|
|
|
5003
5653
|
}
|
|
5004
5654
|
}
|
|
5005
5655
|
}
|
|
5656
|
+
const MARKER_RE = /\[volute:(?:outbound|activity):\d+\]/g;
|
|
5657
|
+
const cleanContent = body.type === "tool_result" && body.content ? body.content.replace(MARKER_RE, "").trimEnd() : body.content;
|
|
5006
5658
|
const db = await getDb();
|
|
5007
5659
|
let insertedId;
|
|
5008
5660
|
try {
|
|
@@ -5012,7 +5664,7 @@ ${user.trimEnd()}
|
|
|
5012
5664
|
session: body.session ?? null,
|
|
5013
5665
|
channel: body.channel ?? null,
|
|
5014
5666
|
message_id: body.messageId ?? null,
|
|
5015
|
-
content:
|
|
5667
|
+
content: cleanContent ?? null,
|
|
5016
5668
|
metadata: body.metadata ? JSON.stringify(body.metadata) : null,
|
|
5017
5669
|
turn_id: turnId ?? null
|
|
5018
5670
|
}).returning({ id: mindHistory.id });
|
|
@@ -5023,13 +5675,21 @@ ${user.trimEnd()}
|
|
|
5023
5675
|
if (body.type === "tool_use" && insertedId != null) {
|
|
5024
5676
|
trackToolUse(baseName, body.session, insertedId);
|
|
5025
5677
|
}
|
|
5678
|
+
if (body.type === "tool_result" && turnId && body.content) {
|
|
5679
|
+
const toolUseEventId = getLastToolUseEventId(baseName, body.session);
|
|
5680
|
+
try {
|
|
5681
|
+
await linkToolResultToTurn(baseName, turnId, body.content, toolUseEventId);
|
|
5682
|
+
} catch (err) {
|
|
5683
|
+
logger_default.error("failed to link outbound to turn", logger_default.errorData(err));
|
|
5684
|
+
}
|
|
5685
|
+
}
|
|
5026
5686
|
publish2(baseName, {
|
|
5027
5687
|
mind: baseName,
|
|
5028
5688
|
type: body.type,
|
|
5029
5689
|
session: body.session,
|
|
5030
5690
|
channel: body.channel,
|
|
5031
5691
|
messageId: body.messageId,
|
|
5032
|
-
content:
|
|
5692
|
+
content: cleanContent,
|
|
5033
5693
|
metadata: body.metadata,
|
|
5034
5694
|
turnId: turnId ?? void 0
|
|
5035
5695
|
});
|
|
@@ -5056,6 +5716,11 @@ ${user.trimEnd()}
|
|
|
5056
5716
|
const busy = body.session ? dm.isSessionBusy(baseName, body.session) : false;
|
|
5057
5717
|
if (!busy) {
|
|
5058
5718
|
const completedTurnId = await completeTurn(baseName, body.session);
|
|
5719
|
+
if (completedTurnId) {
|
|
5720
|
+
tagUntaggedOutbound(baseName, completedTurnId).catch(
|
|
5721
|
+
(err) => logger_default.warn("failed to tag orphaned outbound records", logger_default.errorData(err))
|
|
5722
|
+
);
|
|
5723
|
+
}
|
|
5059
5724
|
if (insertedId != null) {
|
|
5060
5725
|
summarizeTurn(baseName, body.session, body.channel, insertedId, completedTurnId).catch(
|
|
5061
5726
|
(err) => logger_default.error("turn summarization failed", logger_default.errorData(err))
|
|
@@ -5067,6 +5732,11 @@ ${user.trimEnd()}
|
|
|
5067
5732
|
logger_default.error("turn completion check failed", logger_default.errorData(err));
|
|
5068
5733
|
}
|
|
5069
5734
|
const completedTurnId = await completeTurn(baseName, body.session);
|
|
5735
|
+
if (completedTurnId) {
|
|
5736
|
+
tagUntaggedOutbound(baseName, completedTurnId).catch(
|
|
5737
|
+
(err2) => logger_default.warn("failed to tag orphaned outbound records", logger_default.errorData(err2))
|
|
5738
|
+
);
|
|
5739
|
+
}
|
|
5070
5740
|
if (insertedId != null) {
|
|
5071
5741
|
summarizeTurn(baseName, body.session, body.channel, insertedId, completedTurnId).catch(
|
|
5072
5742
|
(err2) => logger_default.error("turn summarization failed", logger_default.errorData(err2))
|
|
@@ -5167,228 +5837,49 @@ ${user.trimEnd()}
|
|
|
5167
5837
|
const db = await getDb();
|
|
5168
5838
|
const rows = await db.select({
|
|
5169
5839
|
session: mindHistory.session,
|
|
5170
|
-
started_at:
|
|
5171
|
-
event_count:
|
|
5172
|
-
message_count:
|
|
5173
|
-
tool_count:
|
|
5174
|
-
}).from(mindHistory).where(
|
|
5840
|
+
started_at: sql3`MIN(${mindHistory.created_at})`,
|
|
5841
|
+
event_count: sql3`COUNT(*)`,
|
|
5842
|
+
message_count: sql3`SUM(CASE WHEN ${mindHistory.type} IN ('inbound','outbound') THEN 1 ELSE 0 END)`,
|
|
5843
|
+
tool_count: sql3`SUM(CASE WHEN ${mindHistory.type}='tool_use' THEN 1 ELSE 0 END)`
|
|
5844
|
+
}).from(mindHistory).where(and5(eq6(mindHistory.mind, name), sql3`${mindHistory.session} IS NOT NULL`)).groupBy(mindHistory.session).orderBy(sql3`MIN(${mindHistory.created_at}) DESC`);
|
|
5175
5845
|
return c.json(rows);
|
|
5176
5846
|
}).get("/:name/history/channels", async (c) => {
|
|
5177
5847
|
const name = c.req.param("name");
|
|
5178
5848
|
const db = await getDb();
|
|
5179
|
-
const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(
|
|
5849
|
+
const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq6(mindHistory.mind, name));
|
|
5180
5850
|
return c.json(rows.map((r) => r.channel));
|
|
5181
5851
|
}).get("/:name/history/export", async (c) => {
|
|
5182
5852
|
const name = c.req.param("name");
|
|
5183
5853
|
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
5184
5854
|
const db = await getDb();
|
|
5185
|
-
const rows = await db.select().from(mindHistory).where(
|
|
5855
|
+
const rows = await db.select().from(mindHistory).where(eq6(mindHistory.mind, name));
|
|
5186
5856
|
return c.json(rows);
|
|
5187
|
-
}).get("/:name/history/
|
|
5857
|
+
}).get("/:name/history/turn", async (c) => {
|
|
5188
5858
|
const name = c.req.param("name");
|
|
5189
|
-
const
|
|
5190
|
-
const
|
|
5191
|
-
const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
|
|
5192
|
-
const db = await getDb();
|
|
5193
|
-
const conditions = [eq5(turns.mind, name)];
|
|
5194
|
-
if (turnIdFilter) conditions.push(eq5(turns.id, turnIdFilter));
|
|
5195
|
-
const turnRows = await db.select().from(turns).where(and4(...conditions)).orderBy(desc3(turns.created_at)).limit(limit).offset(offset);
|
|
5196
|
-
if (turnRows.length === 0) return c.json([]);
|
|
5197
|
-
const turnIds = turnRows.map((t) => t.id);
|
|
5198
|
-
const summaryRows = await db.select().from(mindHistory).where(
|
|
5199
|
-
and4(
|
|
5200
|
-
eq5(mindHistory.mind, name),
|
|
5201
|
-
eq5(mindHistory.type, "summary"),
|
|
5202
|
-
inArray(mindHistory.turn_id, turnIds)
|
|
5203
|
-
)
|
|
5204
|
-
);
|
|
5205
|
-
const summaryByTurn = /* @__PURE__ */ new Map();
|
|
5206
|
-
for (const s of summaryRows) {
|
|
5207
|
-
if (s.turn_id)
|
|
5208
|
-
summaryByTurn.set(s.turn_id, { content: s.content ?? "", metadata: s.metadata });
|
|
5209
|
-
}
|
|
5210
|
-
const msgRows = await db.select({
|
|
5211
|
-
id: messages.id,
|
|
5212
|
-
conversation_id: messages.conversation_id,
|
|
5213
|
-
role: messages.role,
|
|
5214
|
-
sender_name: messages.sender_name,
|
|
5215
|
-
content: messages.content,
|
|
5216
|
-
source_event_id: messages.source_event_id,
|
|
5217
|
-
turn_id: messages.turn_id,
|
|
5218
|
-
created_at: messages.created_at
|
|
5219
|
-
}).from(messages).where(inArray(messages.turn_id, turnIds)).orderBy(messages.created_at);
|
|
5220
|
-
const msgsByTurnConv = /* @__PURE__ */ new Map();
|
|
5221
|
-
const convIds = /* @__PURE__ */ new Set();
|
|
5222
|
-
for (const m of msgRows) {
|
|
5223
|
-
if (!m.turn_id) continue;
|
|
5224
|
-
let byConv = msgsByTurnConv.get(m.turn_id);
|
|
5225
|
-
if (!byConv) {
|
|
5226
|
-
byConv = /* @__PURE__ */ new Map();
|
|
5227
|
-
msgsByTurnConv.set(m.turn_id, byConv);
|
|
5228
|
-
}
|
|
5229
|
-
let arr = byConv.get(m.conversation_id);
|
|
5230
|
-
if (!arr) {
|
|
5231
|
-
arr = [];
|
|
5232
|
-
byConv.set(m.conversation_id, arr);
|
|
5233
|
-
}
|
|
5234
|
-
arr.push(m);
|
|
5235
|
-
convIds.add(m.conversation_id);
|
|
5236
|
-
}
|
|
5237
|
-
const convMeta = /* @__PURE__ */ new Map();
|
|
5238
|
-
const convParticipantsMap = /* @__PURE__ */ new Map();
|
|
5239
|
-
if (convIds.size > 0) {
|
|
5240
|
-
const convIdsArr = [...convIds];
|
|
5241
|
-
const convRows = await db.select({
|
|
5242
|
-
id: conversations.id,
|
|
5243
|
-
type: conversations.type,
|
|
5244
|
-
name: conversations.name,
|
|
5245
|
-
title: conversations.title
|
|
5246
|
-
}).from(conversations).where(inArray(conversations.id, convIdsArr));
|
|
5247
|
-
for (const c2 of convRows) convMeta.set(c2.id, c2);
|
|
5248
|
-
const cpRows = await db.select({
|
|
5249
|
-
conversationId: conversationParticipants.conversation_id,
|
|
5250
|
-
username: users.username,
|
|
5251
|
-
displayName: users.display_name
|
|
5252
|
-
}).from(conversationParticipants).innerJoin(users, eq5(conversationParticipants.user_id, users.id)).where(inArray(conversationParticipants.conversation_id, convIdsArr));
|
|
5253
|
-
for (const cp of cpRows) {
|
|
5254
|
-
let arr = convParticipantsMap.get(cp.conversationId);
|
|
5255
|
-
if (!arr) {
|
|
5256
|
-
arr = [];
|
|
5257
|
-
convParticipantsMap.set(cp.conversationId, arr);
|
|
5258
|
-
}
|
|
5259
|
-
arr.push({ username: cp.username, displayName: cp.displayName });
|
|
5260
|
-
}
|
|
5261
|
-
}
|
|
5262
|
-
function getConvLabel(convId) {
|
|
5263
|
-
const meta = convMeta.get(convId);
|
|
5264
|
-
if (!meta) return "Conversation";
|
|
5265
|
-
if (meta.type === "channel" && meta.name) return `#${meta.name}`;
|
|
5266
|
-
const parts = convParticipantsMap.get(convId) ?? [];
|
|
5267
|
-
if (meta.type === "dm" && parts.length === 2) {
|
|
5268
|
-
const other = parts.find((p) => p.username !== name);
|
|
5269
|
-
if (other) return `@${other.displayName || other.username}`;
|
|
5270
|
-
}
|
|
5271
|
-
if (meta.title) return meta.title;
|
|
5272
|
-
return "Conversation";
|
|
5273
|
-
}
|
|
5274
|
-
const activityRows = await db.select().from(activity).where(inArray(activity.turn_id, turnIds)).orderBy(activity.created_at);
|
|
5275
|
-
const activitiesByTurn = /* @__PURE__ */ new Map();
|
|
5276
|
-
for (const a of activityRows) {
|
|
5277
|
-
if (!a.turn_id) continue;
|
|
5278
|
-
let arr = activitiesByTurn.get(a.turn_id);
|
|
5279
|
-
if (!arr) {
|
|
5280
|
-
arr = [];
|
|
5281
|
-
activitiesByTurn.set(a.turn_id, arr);
|
|
5282
|
-
}
|
|
5283
|
-
arr.push(a);
|
|
5284
|
-
}
|
|
5285
|
-
const triggerIds = turnRows.filter((t) => t.trigger_event_id != null).map((t) => t.trigger_event_id);
|
|
5286
|
-
const triggerMap = /* @__PURE__ */ new Map();
|
|
5287
|
-
if (triggerIds.length > 0) {
|
|
5288
|
-
const triggerRows = await db.select({
|
|
5289
|
-
id: mindHistory.id,
|
|
5290
|
-
channel: mindHistory.channel,
|
|
5291
|
-
sender: mindHistory.sender,
|
|
5292
|
-
content: mindHistory.content
|
|
5293
|
-
}).from(mindHistory).where(inArray(mindHistory.id, triggerIds));
|
|
5294
|
-
for (const r of triggerRows) triggerMap.set(r.id, r);
|
|
5295
|
-
}
|
|
5296
|
-
const result = turnRows.map((t) => {
|
|
5297
|
-
const summary = summaryByTurn.get(t.id);
|
|
5298
|
-
const turnConvs = msgsByTurnConv.get(t.id) ?? /* @__PURE__ */ new Map();
|
|
5299
|
-
const convEntries = [...turnConvs.entries()].map(([convId, msgs]) => {
|
|
5300
|
-
const meta = convMeta.get(convId);
|
|
5301
|
-
return {
|
|
5302
|
-
id: convId,
|
|
5303
|
-
label: getConvLabel(convId),
|
|
5304
|
-
type: meta?.type ?? "dm",
|
|
5305
|
-
messages: msgs.map((m) => {
|
|
5306
|
-
let content;
|
|
5307
|
-
try {
|
|
5308
|
-
const parsed = JSON.parse(m.content);
|
|
5309
|
-
content = Array.isArray(parsed) ? parsed : [{ type: "text", text: m.content }];
|
|
5310
|
-
} catch {
|
|
5311
|
-
content = [{ type: "text", text: m.content }];
|
|
5312
|
-
}
|
|
5313
|
-
return {
|
|
5314
|
-
id: m.id,
|
|
5315
|
-
role: m.role,
|
|
5316
|
-
sender_name: m.sender_name,
|
|
5317
|
-
content,
|
|
5318
|
-
source_event_id: m.source_event_id,
|
|
5319
|
-
created_at: m.created_at
|
|
5320
|
-
};
|
|
5321
|
-
})
|
|
5322
|
-
};
|
|
5323
|
-
});
|
|
5324
|
-
const turnActivities = (activitiesByTurn.get(t.id) ?? []).map((a) => {
|
|
5325
|
-
let metadata = null;
|
|
5326
|
-
if (a.metadata) {
|
|
5327
|
-
try {
|
|
5328
|
-
metadata = JSON.parse(a.metadata);
|
|
5329
|
-
} catch (err) {
|
|
5330
|
-
logger_default.debug(`malformed activity metadata for activity ${a.id}`, logger_default.errorData(err));
|
|
5331
|
-
}
|
|
5332
|
-
}
|
|
5333
|
-
return {
|
|
5334
|
-
id: a.id,
|
|
5335
|
-
type: a.type,
|
|
5336
|
-
summary: a.summary,
|
|
5337
|
-
metadata,
|
|
5338
|
-
source_event_id: a.source_event_id,
|
|
5339
|
-
created_at: a.created_at
|
|
5340
|
-
};
|
|
5341
|
-
});
|
|
5342
|
-
let summaryMeta = null;
|
|
5343
|
-
if (summary?.metadata) {
|
|
5344
|
-
try {
|
|
5345
|
-
summaryMeta = JSON.parse(summary.metadata);
|
|
5346
|
-
} catch (err) {
|
|
5347
|
-
logger_default.debug(`malformed summary metadata for turn ${t.id}`, logger_default.errorData(err));
|
|
5348
|
-
}
|
|
5349
|
-
}
|
|
5350
|
-
const trigger = t.trigger_event_id ? triggerMap.get(t.trigger_event_id) : null;
|
|
5351
|
-
return {
|
|
5352
|
-
id: t.id,
|
|
5353
|
-
summary: summary?.content ?? null,
|
|
5354
|
-
summary_meta: summaryMeta,
|
|
5355
|
-
status: t.status,
|
|
5356
|
-
created_at: t.created_at,
|
|
5357
|
-
trigger: trigger ? { channel: trigger.channel, sender: trigger.sender, content: trigger.content } : null,
|
|
5358
|
-
conversations: convEntries,
|
|
5359
|
-
activities: turnActivities
|
|
5360
|
-
};
|
|
5361
|
-
});
|
|
5362
|
-
return c.json(result);
|
|
5363
|
-
}).get("/:name/history/turn", async (c) => {
|
|
5364
|
-
const name = c.req.param("name");
|
|
5365
|
-
const turnId = c.req.query("turn_id");
|
|
5859
|
+
const turnId = c.req.query("turn_id");
|
|
5860
|
+
const detail = c.req.query("detail") === "1";
|
|
5366
5861
|
const db = await getDb();
|
|
5862
|
+
const typeFilter = detail ? void 0 : sql3`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking','activity')`;
|
|
5863
|
+
let rows;
|
|
5367
5864
|
if (turnId) {
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5865
|
+
rows = await db.select().from(mindHistory).where(and5(eq6(mindHistory.mind, name), eq6(mindHistory.turn_id, turnId), typeFilter)).orderBy(mindHistory.id);
|
|
5866
|
+
} else {
|
|
5867
|
+
const session = c.req.query("session");
|
|
5868
|
+
const fromId = parseInt(c.req.query("from_id") ?? "", 10);
|
|
5869
|
+
const toId = parseInt(c.req.query("to_id") ?? "", 10);
|
|
5870
|
+
if (!session || Number.isNaN(fromId) || Number.isNaN(toId)) {
|
|
5871
|
+
return c.json({ error: "turn_id, or session with from_id and to_id, required" }, 400);
|
|
5872
|
+
}
|
|
5873
|
+
rows = await db.select().from(mindHistory).where(
|
|
5874
|
+
and5(
|
|
5875
|
+
eq6(mindHistory.mind, name),
|
|
5876
|
+
eq6(mindHistory.session, session),
|
|
5877
|
+
sql3`${mindHistory.id} >= ${fromId}`,
|
|
5878
|
+
sql3`${mindHistory.id} <= ${toId}`,
|
|
5879
|
+
typeFilter
|
|
5373
5880
|
)
|
|
5374
5881
|
).orderBy(mindHistory.id);
|
|
5375
|
-
return c.json(rows2);
|
|
5376
5882
|
}
|
|
5377
|
-
const session = c.req.query("session");
|
|
5378
|
-
const fromId = parseInt(c.req.query("from_id") ?? "", 10);
|
|
5379
|
-
const toId = parseInt(c.req.query("to_id") ?? "", 10);
|
|
5380
|
-
if (!session || Number.isNaN(fromId) || Number.isNaN(toId)) {
|
|
5381
|
-
return c.json({ error: "turn_id, or session with from_id and to_id, required" }, 400);
|
|
5382
|
-
}
|
|
5383
|
-
const rows = await db.select().from(mindHistory).where(
|
|
5384
|
-
and4(
|
|
5385
|
-
eq5(mindHistory.mind, name),
|
|
5386
|
-
eq5(mindHistory.session, session),
|
|
5387
|
-
sql2`${mindHistory.id} >= ${fromId}`,
|
|
5388
|
-
sql2`${mindHistory.id} <= ${toId}`,
|
|
5389
|
-
sql2`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking')`
|
|
5390
|
-
)
|
|
5391
|
-
).orderBy(mindHistory.id);
|
|
5392
5883
|
return c.json(rows);
|
|
5393
5884
|
}).get("/:name/history/cross-session", async (c) => {
|
|
5394
5885
|
const name = c.req.param("name");
|
|
@@ -5397,14 +5888,14 @@ ${user.trimEnd()}
|
|
|
5397
5888
|
let sinceTimestamp = null;
|
|
5398
5889
|
if (currentSession) {
|
|
5399
5890
|
const lastTurn = await db.select({ turn_id: mindHistory.turn_id }).from(mindHistory).where(
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5891
|
+
and5(
|
|
5892
|
+
eq6(mindHistory.mind, name),
|
|
5893
|
+
eq6(mindHistory.session, currentSession),
|
|
5894
|
+
sql3`${mindHistory.turn_id} IS NOT NULL`
|
|
5404
5895
|
)
|
|
5405
|
-
).orderBy(
|
|
5896
|
+
).orderBy(desc4(mindHistory.created_at)).limit(1);
|
|
5406
5897
|
if (lastTurn.length > 0 && lastTurn[0].turn_id) {
|
|
5407
|
-
const firstEvent = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(
|
|
5898
|
+
const firstEvent = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(eq6(mindHistory.turn_id, lastTurn[0].turn_id)).orderBy(mindHistory.created_at).limit(1);
|
|
5408
5899
|
if (firstEvent.length > 0) {
|
|
5409
5900
|
sinceTimestamp = firstEvent[0].created_at;
|
|
5410
5901
|
}
|
|
@@ -5414,18 +5905,18 @@ ${user.trimEnd()}
|
|
|
5414
5905
|
sinceTimestamp = new Date(Date.now() - 36e5).toISOString().replace("T", " ").slice(0, 19);
|
|
5415
5906
|
}
|
|
5416
5907
|
const conditions = [
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5908
|
+
eq6(mindHistory.mind, name),
|
|
5909
|
+
eq6(mindHistory.type, "summary"),
|
|
5910
|
+
sql3`${mindHistory.created_at} > ${sinceTimestamp}`
|
|
5420
5911
|
];
|
|
5421
5912
|
if (currentSession) {
|
|
5422
|
-
conditions.push(
|
|
5913
|
+
conditions.push(sql3`${mindHistory.session} != ${currentSession}`);
|
|
5423
5914
|
}
|
|
5424
5915
|
const rows = await db.select({
|
|
5425
5916
|
session: mindHistory.session,
|
|
5426
5917
|
content: mindHistory.content,
|
|
5427
5918
|
created_at: mindHistory.created_at
|
|
5428
|
-
}).from(mindHistory).where(
|
|
5919
|
+
}).from(mindHistory).where(and5(...conditions)).orderBy(desc4(mindHistory.created_at)).limit(50);
|
|
5429
5920
|
if (rows.length === 0) {
|
|
5430
5921
|
return c.json({ context: null });
|
|
5431
5922
|
}
|
|
@@ -5445,40 +5936,40 @@ ${lines.join("\n")}` });
|
|
|
5445
5936
|
const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
|
|
5446
5937
|
const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
|
|
5447
5938
|
const db = await getDb();
|
|
5448
|
-
const conditions = [
|
|
5939
|
+
const conditions = [eq6(mindHistory.mind, name)];
|
|
5449
5940
|
if (channel) {
|
|
5450
|
-
conditions.push(
|
|
5941
|
+
conditions.push(eq6(mindHistory.channel, channel));
|
|
5451
5942
|
}
|
|
5452
5943
|
if (session) {
|
|
5453
|
-
conditions.push(
|
|
5944
|
+
conditions.push(eq6(mindHistory.session, session));
|
|
5454
5945
|
}
|
|
5455
5946
|
const effectivePreset = full ? "all" : preset;
|
|
5456
5947
|
switch (effectivePreset) {
|
|
5457
5948
|
case "all":
|
|
5458
5949
|
break;
|
|
5459
5950
|
case "conversation":
|
|
5460
|
-
conditions.push(
|
|
5951
|
+
conditions.push(sql3`${mindHistory.type} IN ('summary','inbound','outbound','tool_use')`);
|
|
5461
5952
|
break;
|
|
5462
5953
|
case "detailed":
|
|
5463
5954
|
conditions.push(
|
|
5464
|
-
|
|
5955
|
+
sql3`${mindHistory.type} IN ('summary','inbound','outbound','tool_use','tool_result','text','thinking')`
|
|
5465
5956
|
);
|
|
5466
5957
|
break;
|
|
5467
5958
|
default:
|
|
5468
|
-
conditions.push(
|
|
5959
|
+
conditions.push(sql3`${mindHistory.type} IN ('summary')`);
|
|
5469
5960
|
break;
|
|
5470
5961
|
}
|
|
5471
|
-
const rows = await db.select().from(mindHistory).where(
|
|
5962
|
+
const rows = await db.select().from(mindHistory).where(and5(...conditions)).orderBy(desc4(mindHistory.created_at)).limit(limit).offset(offset);
|
|
5472
5963
|
return c.json(rows);
|
|
5473
5964
|
});
|
|
5474
5965
|
var minds_default = app13;
|
|
5475
5966
|
|
|
5476
5967
|
// src/web/api/prompts.ts
|
|
5477
5968
|
import { zValidator as zValidator6 } from "@hono/zod-validator";
|
|
5478
|
-
import { eq as
|
|
5479
|
-
import { Hono as
|
|
5969
|
+
import { eq as eq7, sql as sql4 } from "drizzle-orm";
|
|
5970
|
+
import { Hono as Hono16 } from "hono";
|
|
5480
5971
|
import { z as z6 } from "zod";
|
|
5481
|
-
var app14 = new
|
|
5972
|
+
var app14 = new Hono16().get("/", async (c) => {
|
|
5482
5973
|
let rows;
|
|
5483
5974
|
try {
|
|
5484
5975
|
const db = await getDb();
|
|
@@ -5508,9 +5999,9 @@ var app14 = new Hono15().get("/", async (c) => {
|
|
|
5508
5999
|
}
|
|
5509
6000
|
const { content } = c.req.valid("json");
|
|
5510
6001
|
const db = await getDb();
|
|
5511
|
-
await db.insert(systemPrompts).values({ key, content, updated_at:
|
|
6002
|
+
await db.insert(systemPrompts).values({ key, content, updated_at: sql4`(datetime('now'))` }).onConflictDoUpdate({
|
|
5512
6003
|
target: systemPrompts.key,
|
|
5513
|
-
set: { content, updated_at:
|
|
6004
|
+
set: { content, updated_at: sql4`(datetime('now'))` }
|
|
5514
6005
|
});
|
|
5515
6006
|
return c.json({ ok: true });
|
|
5516
6007
|
}).delete("/:key", requireAdmin, async (c) => {
|
|
@@ -5519,7 +6010,7 @@ var app14 = new Hono15().get("/", async (c) => {
|
|
|
5519
6010
|
return c.json({ error: "Unknown prompt key" }, 404);
|
|
5520
6011
|
}
|
|
5521
6012
|
const db = await getDb();
|
|
5522
|
-
await db.delete(systemPrompts).where(
|
|
6013
|
+
await db.delete(systemPrompts).where(eq7(systemPrompts.key, key));
|
|
5523
6014
|
return c.json({ ok: true });
|
|
5524
6015
|
});
|
|
5525
6016
|
var prompts_default = app14;
|
|
@@ -5527,7 +6018,7 @@ var prompts_default = app14;
|
|
|
5527
6018
|
// src/web/api/public-files.ts
|
|
5528
6019
|
import { readdir, readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
5529
6020
|
import { extname as extname3, resolve as resolve13 } from "path";
|
|
5530
|
-
import { Hono as
|
|
6021
|
+
import { Hono as Hono17 } from "hono";
|
|
5531
6022
|
var MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
5532
6023
|
async function resolvePublicRoot(name) {
|
|
5533
6024
|
if (name === "_system") return resolve13(voluteHome(), "shared");
|
|
@@ -5568,7 +6059,7 @@ async function listDir(dirPath) {
|
|
|
5568
6059
|
type: e.isDirectory() ? "directory" : "file"
|
|
5569
6060
|
}));
|
|
5570
6061
|
}
|
|
5571
|
-
var app15 = new
|
|
6062
|
+
var app15 = new Hono17().get("/:name/", async (c) => {
|
|
5572
6063
|
const name = c.req.param("name");
|
|
5573
6064
|
const publicRoot = await resolvePublicRoot(name);
|
|
5574
6065
|
if (!publicRoot) return c.json({ error: "Not found" }, 404);
|
|
@@ -5615,7 +6106,7 @@ var public_files_default = app15;
|
|
|
5615
6106
|
|
|
5616
6107
|
// src/web/api/schedules.ts
|
|
5617
6108
|
import { CronExpressionParser } from "cron-parser";
|
|
5618
|
-
import { Hono as
|
|
6109
|
+
import { Hono as Hono18 } from "hono";
|
|
5619
6110
|
var slog2 = logger_default.child("schedules");
|
|
5620
6111
|
function readSchedules(name) {
|
|
5621
6112
|
return readVoluteConfig(mindDir(name))?.schedules ?? [];
|
|
@@ -5633,7 +6124,7 @@ function writeSchedules(name, schedules) {
|
|
|
5633
6124
|
data: { schedules }
|
|
5634
6125
|
});
|
|
5635
6126
|
}
|
|
5636
|
-
var app16 = new
|
|
6127
|
+
var app16 = new Hono18().get("/:name/clock/status", async (c) => {
|
|
5637
6128
|
const name = c.req.param("name");
|
|
5638
6129
|
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
5639
6130
|
const sleepManager = getSleepManagerIfReady();
|
|
@@ -5643,6 +6134,7 @@ var app16 = new Hono17().get("/:name/clock/status", async (c) => {
|
|
|
5643
6134
|
const now = /* @__PURE__ */ new Date();
|
|
5644
6135
|
const in24h = new Date(now.getTime() + 24 * 60 * 6e4);
|
|
5645
6136
|
const upcoming = [];
|
|
6137
|
+
const previous = [];
|
|
5646
6138
|
for (const s of schedules) {
|
|
5647
6139
|
if (!s.enabled) continue;
|
|
5648
6140
|
if (s.fireAt) {
|
|
@@ -5660,10 +6152,17 @@ var app16 = new Hono17().get("/:name/clock/status", async (c) => {
|
|
|
5660
6152
|
} catch {
|
|
5661
6153
|
slog2.warn(`invalid cron "${s.cron}" for schedule "${s.id}" of ${name}`);
|
|
5662
6154
|
}
|
|
6155
|
+
try {
|
|
6156
|
+
const prevInterval = CronExpressionParser.parse(s.cron);
|
|
6157
|
+
const prev = prevInterval.prev().toDate();
|
|
6158
|
+
previous.push({ id: s.id, at: prev.toISOString() });
|
|
6159
|
+
} catch {
|
|
6160
|
+
}
|
|
5663
6161
|
}
|
|
5664
6162
|
}
|
|
5665
6163
|
upcoming.sort((a, b) => a.at.localeCompare(b.at));
|
|
5666
|
-
|
|
6164
|
+
previous.sort((a, b) => b.at.localeCompare(a.at));
|
|
6165
|
+
return c.json({ sleep: sleepState, sleepConfig, schedules, upcoming, previous });
|
|
5667
6166
|
}).get("/:name/schedules", async (c) => {
|
|
5668
6167
|
const name = c.req.param("name");
|
|
5669
6168
|
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -5791,7 +6290,7 @@ var app16 = new Hono17().get("/:name/clock/status", async (c) => {
|
|
|
5791
6290
|
const body = await c.req.text();
|
|
5792
6291
|
const message = `[webhook: ${event}] ${body}`;
|
|
5793
6292
|
try {
|
|
5794
|
-
const { sendSystemMessage } = await import("./system-chat-
|
|
6293
|
+
const { sendSystemMessage } = await import("./system-chat-JAPOJ3KE.js");
|
|
5795
6294
|
await sendSystemMessage(name, message);
|
|
5796
6295
|
return c.json({ ok: true });
|
|
5797
6296
|
} catch (err) {
|
|
@@ -5805,10 +6304,10 @@ var schedules_default = app16;
|
|
|
5805
6304
|
import { mkdirSync as mkdirSync7 } from "fs";
|
|
5806
6305
|
import { homedir as homedir2 } from "os";
|
|
5807
6306
|
import { resolve as resolve14 } from "path";
|
|
5808
|
-
import { Hono as
|
|
6307
|
+
import { Hono as Hono19 } from "hono";
|
|
5809
6308
|
import { setCookie as setCookie2 } from "hono/cookie";
|
|
5810
6309
|
var DEFAULT_API_URL2 = "https://volute.systems";
|
|
5811
|
-
var setup = new
|
|
6310
|
+
var setup = new Hono19();
|
|
5812
6311
|
function writeSetupConfig(systemName, description) {
|
|
5813
6312
|
const configHome = process.env.VOLUTE_HOME ?? resolve14(homedir2(), ".volute");
|
|
5814
6313
|
const mindsDir = resolve14(configHome, "minds");
|
|
@@ -5844,7 +6343,7 @@ setup.get("/status", async (c) => {
|
|
|
5844
6343
|
let hasAccount = false;
|
|
5845
6344
|
if (hasSystem) {
|
|
5846
6345
|
try {
|
|
5847
|
-
const { listUsersByType: listUsersByType2 } = await import("./auth-
|
|
6346
|
+
const { listUsersByType: listUsersByType2 } = await import("./auth-GKCDSO4T.js");
|
|
5848
6347
|
const brains = await listUsersByType2("brain");
|
|
5849
6348
|
hasAccount = brains.length > 0;
|
|
5850
6349
|
} catch (err) {
|
|
@@ -6029,7 +6528,7 @@ setup.post("/account", async (c) => {
|
|
|
6029
6528
|
}
|
|
6030
6529
|
}
|
|
6031
6530
|
try {
|
|
6032
|
-
const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-
|
|
6531
|
+
const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-GKCDSO4T.js");
|
|
6033
6532
|
const user = await createUser2(body.username.trim(), body.password);
|
|
6034
6533
|
if (body.displayName?.trim()) {
|
|
6035
6534
|
await updateUserProfile2(user.id, { display_name: body.displayName.trim() });
|
|
@@ -6075,13 +6574,13 @@ setup.post("/models", async (c) => {
|
|
|
6075
6574
|
return c.json({ error: "Spirit model is required" }, 400);
|
|
6076
6575
|
}
|
|
6077
6576
|
try {
|
|
6078
|
-
const { setEnabledModels:
|
|
6079
|
-
|
|
6577
|
+
const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-SBY2WG7O.js");
|
|
6578
|
+
setEnabledModels3(body.models);
|
|
6080
6579
|
const config2 = readGlobalConfig();
|
|
6081
6580
|
config2.spiritModel = body.spiritModel.trim();
|
|
6082
6581
|
writeGlobalConfig(config2);
|
|
6083
6582
|
if (body.utilityModel?.trim()) {
|
|
6084
|
-
|
|
6583
|
+
setUtilityModel2(body.utilityModel.trim());
|
|
6085
6584
|
}
|
|
6086
6585
|
return c.json({ ok: true });
|
|
6087
6586
|
} catch (err) {
|
|
@@ -6093,8 +6592,8 @@ setup.post("/complete", async (c) => {
|
|
|
6093
6592
|
return c.json({ error: "Setup already complete" }, 400);
|
|
6094
6593
|
}
|
|
6095
6594
|
try {
|
|
6096
|
-
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-
|
|
6097
|
-
const { startSpiritFull } = await import("./mind-service-
|
|
6595
|
+
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-VRONKFMF.js");
|
|
6596
|
+
const { startSpiritFull } = await import("./mind-service-2MQ6UK5N.js");
|
|
6098
6597
|
await ensureSpiritProject();
|
|
6099
6598
|
await syncSpiritTemplate();
|
|
6100
6599
|
const warnings = [];
|
|
@@ -6110,8 +6609,8 @@ setup.post("/complete", async (c) => {
|
|
|
6110
6609
|
}
|
|
6111
6610
|
let spiritConversationId;
|
|
6112
6611
|
try {
|
|
6113
|
-
const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-
|
|
6114
|
-
const { createConversation: createConversation6, findDMConversation: findDMConversation2 } = await import("./conversations-
|
|
6612
|
+
const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-GKCDSO4T.js");
|
|
6613
|
+
const { createConversation: createConversation6, findDMConversation: findDMConversation2 } = await import("./conversations-AWI5SZW2.js");
|
|
6115
6614
|
const spiritUser = await getOrCreateMindUser2("volute");
|
|
6116
6615
|
const brains = await listUsersByType2("brain");
|
|
6117
6616
|
const admin2 = brains.find((u) => u.role === "admin");
|
|
@@ -6134,8 +6633,8 @@ setup.post("/complete", async (c) => {
|
|
|
6134
6633
|
logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
|
|
6135
6634
|
if (spiritConversationId && spiritStarted) {
|
|
6136
6635
|
try {
|
|
6137
|
-
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-
|
|
6138
|
-
const { listUsersByType: listUsers6 } = await import("./auth-
|
|
6636
|
+
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-DFF5SJRM.js");
|
|
6637
|
+
const { listUsersByType: listUsers6 } = await import("./auth-GKCDSO4T.js");
|
|
6139
6638
|
const admins = await listUsers6("brain");
|
|
6140
6639
|
const admin2 = admins.find((u) => u.role === "admin");
|
|
6141
6640
|
const adminName = admin2?.display_name || admin2?.username || "the admin";
|
|
@@ -6172,8 +6671,8 @@ setup.post("/complete", async (c) => {
|
|
|
6172
6671
|
var setup_default = setup;
|
|
6173
6672
|
|
|
6174
6673
|
// src/web/api/shared.ts
|
|
6175
|
-
import { Hono as
|
|
6176
|
-
var app17 = new
|
|
6674
|
+
import { Hono as Hono20 } from "hono";
|
|
6675
|
+
var app17 = new Hono20().post("/:name/shared/merge", requireAdmin, async (c) => {
|
|
6177
6676
|
const name = c.req.param("name");
|
|
6178
6677
|
const entry = await findMind(name);
|
|
6179
6678
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -6198,8 +6697,8 @@ import { existsSync as existsSync10, mkdtempSync, readdirSync as readdirSync3, r
|
|
|
6198
6697
|
import { tmpdir } from "os";
|
|
6199
6698
|
import { join, resolve as resolve15 } from "path";
|
|
6200
6699
|
import AdmZip from "adm-zip";
|
|
6201
|
-
import { Hono as
|
|
6202
|
-
var app18 = new
|
|
6700
|
+
import { Hono as Hono21 } from "hono";
|
|
6701
|
+
var app18 = new Hono21().get("/", async (c) => {
|
|
6203
6702
|
const skills = await listSharedSkills();
|
|
6204
6703
|
return c.json(skills);
|
|
6205
6704
|
}).get("/defaults/list", async (c) => {
|
|
@@ -6314,14 +6813,14 @@ var skills_default = app18;
|
|
|
6314
6813
|
|
|
6315
6814
|
// src/web/api/typing.ts
|
|
6316
6815
|
import { zValidator as zValidator7 } from "@hono/zod-validator";
|
|
6317
|
-
import { Hono as
|
|
6816
|
+
import { Hono as Hono22 } from "hono";
|
|
6318
6817
|
import { z as z7 } from "zod";
|
|
6319
6818
|
var typingSchema = z7.object({
|
|
6320
6819
|
channel: z7.string().min(1),
|
|
6321
6820
|
sender: z7.string().min(1),
|
|
6322
6821
|
active: z7.boolean()
|
|
6323
6822
|
});
|
|
6324
|
-
var app19 = new
|
|
6823
|
+
var app19 = new Hono22().post("/:name/typing", zValidator7("json", typingSchema), (c) => {
|
|
6325
6824
|
const { channel, sender, active } = c.req.valid("json");
|
|
6326
6825
|
const map = getTypingMap();
|
|
6327
6826
|
if (active) {
|
|
@@ -6345,9 +6844,9 @@ var typing_default = app19;
|
|
|
6345
6844
|
|
|
6346
6845
|
// src/web/api/update.ts
|
|
6347
6846
|
import { spawn as spawn3 } from "child_process";
|
|
6348
|
-
import { Hono as
|
|
6847
|
+
import { Hono as Hono23 } from "hono";
|
|
6349
6848
|
var bin;
|
|
6350
|
-
var app20 = new
|
|
6849
|
+
var app20 = new Hono23().get("/update", async (c) => {
|
|
6351
6850
|
const result = await checkForUpdate();
|
|
6352
6851
|
return c.json(result);
|
|
6353
6852
|
}).post("/update", requireAdmin, async (c) => {
|
|
@@ -6364,219 +6863,15 @@ var app20 = new Hono22().get("/update", async (c) => {
|
|
|
6364
6863
|
});
|
|
6365
6864
|
var update_default = app20;
|
|
6366
6865
|
|
|
6367
|
-
// src/web/api/v1/chat.ts
|
|
6368
|
-
import { zValidator as zValidator8 } from "@hono/zod-validator";
|
|
6369
|
-
import { Hono as Hono23 } from "hono";
|
|
6370
|
-
import { streamSSE as streamSSE4 } from "hono/streaming";
|
|
6371
|
-
import { z as z8 } from "zod";
|
|
6372
|
-
async function fanOutToMinds(opts) {
|
|
6373
|
-
const participants = await getParticipants(opts.conversationId);
|
|
6374
|
-
const mindParticipants = participants.filter(
|
|
6375
|
-
(p) => p.userType === "mind" || p.userType === "system"
|
|
6376
|
-
);
|
|
6377
|
-
const participantNames = participants.map((p) => p.username);
|
|
6378
|
-
const isDM = opts.isDM ?? participants.length === 2;
|
|
6379
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-BNCMGYXW.js");
|
|
6380
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-53DZOWW7.js");
|
|
6381
|
-
const manager = getMindManager2();
|
|
6382
|
-
const sm = getSleepManagerIfReady2();
|
|
6383
|
-
const targetMinds = mindParticipants.map((ap) => {
|
|
6384
|
-
const key = opts.targetName ? opts.targetName(ap.username) : ap.username;
|
|
6385
|
-
if (manager.isRunning(key) || sm?.isSleeping(ap.username)) return ap.username;
|
|
6386
|
-
return null;
|
|
6387
|
-
}).filter((n) => n !== null && n !== opts.senderName);
|
|
6388
|
-
function slugForMind(mindUsername) {
|
|
6389
|
-
return buildVoluteSlug({
|
|
6390
|
-
participants,
|
|
6391
|
-
mindUsername,
|
|
6392
|
-
convTitle: opts.convTitle,
|
|
6393
|
-
conversationId: opts.conversationId,
|
|
6394
|
-
...opts.slugExtra
|
|
6395
|
-
});
|
|
6396
|
-
}
|
|
6397
|
-
for (const mindName of targetMinds) {
|
|
6398
|
-
const target = opts.targetName ? opts.targetName(mindName) : mindName;
|
|
6399
|
-
const channel = slugForMind(mindName);
|
|
6400
|
-
const typingMap = getTypingMap();
|
|
6401
|
-
const currentlyTyping = typingMap.get(channel).filter((name) => participantNames.includes(name));
|
|
6402
|
-
deliverMessage(target, {
|
|
6403
|
-
content: opts.contentBlocks,
|
|
6404
|
-
channel,
|
|
6405
|
-
conversationId: opts.conversationId,
|
|
6406
|
-
sender: opts.senderName,
|
|
6407
|
-
participants: participantNames,
|
|
6408
|
-
participantCount: participants.length,
|
|
6409
|
-
isDM,
|
|
6410
|
-
...currentlyTyping.length > 0 ? { typing: currentlyTyping } : {}
|
|
6411
|
-
}).catch((err) => {
|
|
6412
|
-
logger_default.warn("[v1-chat] delivery failed", logger_default.errorData(err));
|
|
6413
|
-
});
|
|
6414
|
-
}
|
|
6415
|
-
}
|
|
6416
|
-
var mindChatSchema = z8.object({
|
|
6417
|
-
message: z8.string().optional(),
|
|
6418
|
-
conversationId: z8.string().optional(),
|
|
6419
|
-
sender: z8.string().optional(),
|
|
6420
|
-
images: z8.array(z8.object({ media_type: z8.string(), data: z8.string() })).optional()
|
|
6421
|
-
});
|
|
6422
|
-
var unifiedChatSchema = z8.object({
|
|
6423
|
-
message: z8.string().optional(),
|
|
6424
|
-
conversationId: z8.string(),
|
|
6425
|
-
images: z8.array(z8.object({ media_type: z8.string(), data: z8.string() })).optional()
|
|
6426
|
-
});
|
|
6427
|
-
var app21 = new Hono23().use("*", authMiddleware).post("/minds/:name/chat", zValidator8("json", mindChatSchema), async (c) => {
|
|
6428
|
-
const name = c.req.param("name");
|
|
6429
|
-
const baseName = await getBaseName(name);
|
|
6430
|
-
const entry = await findMind(baseName);
|
|
6431
|
-
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
6432
|
-
const body = c.req.valid("json");
|
|
6433
|
-
if (!body.message && (!body.images || body.images.length === 0)) {
|
|
6434
|
-
return c.json({ error: "message or images required" }, 400);
|
|
6435
|
-
}
|
|
6436
|
-
const user = c.get("user");
|
|
6437
|
-
const mindUser = await getOrCreateMindUser(baseName);
|
|
6438
|
-
const senderName = user.id === 0 && body.sender ? body.sender : user.username;
|
|
6439
|
-
let conversationId = body.conversationId;
|
|
6440
|
-
if (conversationId) {
|
|
6441
|
-
if (user.id !== 0 && !await isParticipantOrOwner(conversationId, user.id)) {
|
|
6442
|
-
return c.json({ error: "Conversation not found" }, 404);
|
|
6443
|
-
}
|
|
6444
|
-
} else {
|
|
6445
|
-
const participantIds = [];
|
|
6446
|
-
if (user.id !== 0) {
|
|
6447
|
-
participantIds.push(user.id);
|
|
6448
|
-
} else if (body.sender) {
|
|
6449
|
-
const senderMind = await findMind(body.sender);
|
|
6450
|
-
if (senderMind) {
|
|
6451
|
-
const senderMindUser = await getOrCreateMindUser(body.sender);
|
|
6452
|
-
participantIds.push(senderMindUser.id);
|
|
6453
|
-
}
|
|
6454
|
-
}
|
|
6455
|
-
participantIds.push(mindUser.id);
|
|
6456
|
-
if (participantIds.length === 2) {
|
|
6457
|
-
const existing = await findDMConversation(baseName, participantIds);
|
|
6458
|
-
if (existing) conversationId = existing;
|
|
6459
|
-
}
|
|
6460
|
-
if (!conversationId) {
|
|
6461
|
-
const participantNames = /* @__PURE__ */ new Set([senderName, baseName]);
|
|
6462
|
-
const title = [...participantNames].join(", ");
|
|
6463
|
-
const conv2 = await createConversation(baseName, "volute", {
|
|
6464
|
-
userId: user.id !== 0 ? user.id : void 0,
|
|
6465
|
-
title,
|
|
6466
|
-
participantIds
|
|
6467
|
-
});
|
|
6468
|
-
conversationId = conv2.id;
|
|
6469
|
-
}
|
|
6470
|
-
}
|
|
6471
|
-
const conv = await getConversation(conversationId);
|
|
6472
|
-
const convTitle = conv?.title ?? null;
|
|
6473
|
-
const contentBlocks = [];
|
|
6474
|
-
if (body.message) contentBlocks.push({ type: "text", text: body.message });
|
|
6475
|
-
if (body.images) {
|
|
6476
|
-
for (const img of body.images) {
|
|
6477
|
-
contentBlocks.push({ type: "image", media_type: img.media_type, data: img.data });
|
|
6478
|
-
}
|
|
6479
|
-
}
|
|
6480
|
-
const senderIsMind = user.id === 0 && body.sender && await findMind(body.sender);
|
|
6481
|
-
const v1MindSession = c.get("mindSession");
|
|
6482
|
-
let v1SourceEventId;
|
|
6483
|
-
let v1TurnId;
|
|
6484
|
-
if (senderIsMind) {
|
|
6485
|
-
v1SourceEventId = getLastToolUseEventId(body.sender, v1MindSession);
|
|
6486
|
-
v1TurnId = getActiveTurnId(body.sender, v1MindSession);
|
|
6487
|
-
}
|
|
6488
|
-
await addMessage(conversationId, "user", senderName, contentBlocks, {
|
|
6489
|
-
sourceEventId: v1SourceEventId,
|
|
6490
|
-
turnId: v1TurnId
|
|
6491
|
-
});
|
|
6492
|
-
const isDM = conv?.type === "dm";
|
|
6493
|
-
await fanOutToMinds({
|
|
6494
|
-
conversationId,
|
|
6495
|
-
contentBlocks,
|
|
6496
|
-
senderName,
|
|
6497
|
-
convTitle,
|
|
6498
|
-
isDM,
|
|
6499
|
-
slugExtra: conv ? { convType: conv.type, convName: conv.name } : void 0,
|
|
6500
|
-
targetName: (username) => username === baseName ? name : username
|
|
6501
|
-
});
|
|
6502
|
-
return c.json({ ok: true, conversationId });
|
|
6503
|
-
}).get("/minds/:name/conversations/:id/events", async (c) => {
|
|
6504
|
-
const conversationId = c.req.param("id");
|
|
6505
|
-
const user = c.get("user");
|
|
6506
|
-
if (user.id !== 0 && !await isParticipantOrOwner(conversationId, user.id)) {
|
|
6507
|
-
return c.json({ error: "Conversation not found" }, 404);
|
|
6508
|
-
}
|
|
6509
|
-
return streamSSE4(c, async (stream) => {
|
|
6510
|
-
const unsubscribe = subscribe2(conversationId, (event) => {
|
|
6511
|
-
stream.writeSSE({ data: JSON.stringify(event) }).catch((err) => {
|
|
6512
|
-
if (!stream.aborted) logger_default.error("[v1-chat] SSE write error:", logger_default.errorData(err));
|
|
6513
|
-
});
|
|
6514
|
-
});
|
|
6515
|
-
const keepAlive = setInterval(() => {
|
|
6516
|
-
stream.writeSSE({ data: "" }).catch((err) => {
|
|
6517
|
-
if (!stream.aborted) logger_default.error("[v1-chat] SSE ping error:", logger_default.errorData(err));
|
|
6518
|
-
});
|
|
6519
|
-
}, 15e3);
|
|
6520
|
-
await new Promise((resolve20) => {
|
|
6521
|
-
stream.onAbort(() => {
|
|
6522
|
-
unsubscribe();
|
|
6523
|
-
clearInterval(keepAlive);
|
|
6524
|
-
resolve20();
|
|
6525
|
-
});
|
|
6526
|
-
});
|
|
6527
|
-
});
|
|
6528
|
-
}).post("/chat", zValidator8("json", unifiedChatSchema), async (c) => {
|
|
6529
|
-
const user = c.get("user");
|
|
6530
|
-
const body = c.req.valid("json");
|
|
6531
|
-
if (!body.message && (!body.images || body.images.length === 0)) {
|
|
6532
|
-
return c.json({ error: "message or images required" }, 400);
|
|
6533
|
-
}
|
|
6534
|
-
const conv = await getConversation(body.conversationId);
|
|
6535
|
-
if (!conv) return c.json({ error: "Conversation not found" }, 404);
|
|
6536
|
-
if (user.id !== 0 && !await isParticipantOrOwner(body.conversationId, user.id)) {
|
|
6537
|
-
return c.json({ error: "Conversation not found" }, 404);
|
|
6538
|
-
}
|
|
6539
|
-
const senderName = user.username;
|
|
6540
|
-
const contentBlocks = [];
|
|
6541
|
-
if (body.message) contentBlocks.push({ type: "text", text: body.message });
|
|
6542
|
-
if (body.images) {
|
|
6543
|
-
for (const img of body.images) {
|
|
6544
|
-
contentBlocks.push({ type: "image", media_type: img.media_type, data: img.data });
|
|
6545
|
-
}
|
|
6546
|
-
}
|
|
6547
|
-
const unifiedV1Session = c.get("mindSession");
|
|
6548
|
-
let unifiedV1SourceEventId;
|
|
6549
|
-
let unifiedV1TurnId;
|
|
6550
|
-
if (user.user_type === "mind") {
|
|
6551
|
-
unifiedV1SourceEventId = getLastToolUseEventId(senderName, unifiedV1Session);
|
|
6552
|
-
unifiedV1TurnId = getActiveTurnId(senderName, unifiedV1Session);
|
|
6553
|
-
}
|
|
6554
|
-
await addMessage(body.conversationId, "user", senderName, contentBlocks, {
|
|
6555
|
-
sourceEventId: unifiedV1SourceEventId,
|
|
6556
|
-
turnId: unifiedV1TurnId
|
|
6557
|
-
});
|
|
6558
|
-
const isDM = conv.type === "dm";
|
|
6559
|
-
await fanOutToMinds({
|
|
6560
|
-
conversationId: body.conversationId,
|
|
6561
|
-
contentBlocks,
|
|
6562
|
-
senderName,
|
|
6563
|
-
convTitle: conv.title,
|
|
6564
|
-
isDM,
|
|
6565
|
-
slugExtra: { convType: conv.type, convName: conv.name }
|
|
6566
|
-
});
|
|
6567
|
-
return c.json({ ok: true, conversationId: body.conversationId });
|
|
6568
|
-
});
|
|
6569
|
-
var chat_default = app21;
|
|
6570
|
-
|
|
6571
6866
|
// src/web/api/v1/conversations.ts
|
|
6572
|
-
import { zValidator as
|
|
6867
|
+
import { zValidator as zValidator8 } from "@hono/zod-validator";
|
|
6573
6868
|
import { Hono as Hono24 } from "hono";
|
|
6574
|
-
import { z as
|
|
6575
|
-
var createSchema =
|
|
6576
|
-
title:
|
|
6577
|
-
participantNames:
|
|
6869
|
+
import { z as z8 } from "zod";
|
|
6870
|
+
var createSchema = z8.object({
|
|
6871
|
+
title: z8.string().optional(),
|
|
6872
|
+
participantNames: z8.array(z8.string()).min(1)
|
|
6578
6873
|
});
|
|
6579
|
-
var
|
|
6874
|
+
var app21 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
6580
6875
|
const user = c.get("user");
|
|
6581
6876
|
const convs = await listConversationsWithParticipants(user.id);
|
|
6582
6877
|
return c.json(convs);
|
|
@@ -6603,7 +6898,7 @@ var app22 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
6603
6898
|
}
|
|
6604
6899
|
const participants = await getParticipants(id);
|
|
6605
6900
|
return c.json(participants);
|
|
6606
|
-
}).post("/",
|
|
6901
|
+
}).post("/", zValidator8("json", createSchema), async (c) => {
|
|
6607
6902
|
const user = c.get("user");
|
|
6608
6903
|
const body = c.req.valid("json");
|
|
6609
6904
|
const participantIds = /* @__PURE__ */ new Set();
|
|
@@ -6627,6 +6922,9 @@ var app22 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
6627
6922
|
if (!firstMindName) {
|
|
6628
6923
|
return c.json({ error: "At least one mind participant is required" }, 400);
|
|
6629
6924
|
}
|
|
6925
|
+
if (participantIds.size > 2) {
|
|
6926
|
+
return c.json({ error: "Use channels for multi-participant conversations" }, 400);
|
|
6927
|
+
}
|
|
6630
6928
|
const conv = await createConversation(firstMindName, "volute", {
|
|
6631
6929
|
userId: user.id !== 0 ? user.id : void 0,
|
|
6632
6930
|
title: body.title,
|
|
@@ -6642,6 +6940,15 @@ var app22 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
6642
6940
|
}
|
|
6643
6941
|
await markConversationRead(user.id, id);
|
|
6644
6942
|
return c.json({ ok: true });
|
|
6943
|
+
}).put("/:id/private", zValidator8("json", z8.object({ private: z8.boolean() })), async (c) => {
|
|
6944
|
+
const id = c.req.param("id");
|
|
6945
|
+
const user = c.get("user");
|
|
6946
|
+
if (!await isParticipantOrOwner(id, user.id)) {
|
|
6947
|
+
return c.json({ error: "Forbidden" }, 403);
|
|
6948
|
+
}
|
|
6949
|
+
const body = c.req.valid("json");
|
|
6950
|
+
await setConversationPrivate(id, body.private);
|
|
6951
|
+
return c.json({ ok: true });
|
|
6645
6952
|
}).delete("/:id", async (c) => {
|
|
6646
6953
|
const id = c.req.param("id");
|
|
6647
6954
|
const user = c.get("user");
|
|
@@ -6649,12 +6956,12 @@ var app22 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
6649
6956
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
6650
6957
|
return c.json({ ok: true });
|
|
6651
6958
|
});
|
|
6652
|
-
var conversations_default =
|
|
6959
|
+
var conversations_default = app21;
|
|
6653
6960
|
|
|
6654
6961
|
// src/web/api/v1/events.ts
|
|
6655
|
-
import { desc as
|
|
6962
|
+
import { desc as desc5 } from "drizzle-orm";
|
|
6656
6963
|
import { Hono as Hono25 } from "hono";
|
|
6657
|
-
import { streamSSE as
|
|
6964
|
+
import { streamSSE as streamSSE4 } from "hono/streaming";
|
|
6658
6965
|
|
|
6659
6966
|
// src/lib/events/brain-presence.ts
|
|
6660
6967
|
var connections = /* @__PURE__ */ new Map();
|
|
@@ -6700,11 +7007,11 @@ function getEventsSince(sinceId) {
|
|
|
6700
7007
|
}
|
|
6701
7008
|
|
|
6702
7009
|
// src/web/api/v1/events.ts
|
|
6703
|
-
var
|
|
7010
|
+
var app22 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
|
|
6704
7011
|
const user = c.get("user");
|
|
6705
7012
|
const since = c.req.query("since");
|
|
6706
7013
|
const sinceId = since ? Number(since) : 0;
|
|
6707
|
-
return
|
|
7014
|
+
return streamSSE4(c, async (stream) => {
|
|
6708
7015
|
const cleanups = [];
|
|
6709
7016
|
if (user.user_type === "brain") {
|
|
6710
7017
|
addConnection(user.username);
|
|
@@ -6723,7 +7030,7 @@ var app23 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
|
|
|
6723
7030
|
let recentActivity = [];
|
|
6724
7031
|
try {
|
|
6725
7032
|
const db = await getDb();
|
|
6726
|
-
recentActivity = await db.select().from(activity).orderBy(
|
|
7033
|
+
recentActivity = await db.select().from(activity).orderBy(desc5(activity.created_at)).limit(50);
|
|
6727
7034
|
recentActivity = recentActivity.map((row) => ({
|
|
6728
7035
|
...row,
|
|
6729
7036
|
metadata: row.metadata ? JSON.parse(row.metadata) : null
|
|
@@ -6803,7 +7110,7 @@ var app23 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
|
|
|
6803
7110
|
}
|
|
6804
7111
|
});
|
|
6805
7112
|
});
|
|
6806
|
-
var events_default =
|
|
7113
|
+
var events_default = app22;
|
|
6807
7114
|
|
|
6808
7115
|
// src/web/api/variants.ts
|
|
6809
7116
|
import { existsSync as existsSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
@@ -6924,7 +7231,7 @@ async function verify2(port) {
|
|
|
6924
7231
|
}
|
|
6925
7232
|
|
|
6926
7233
|
// src/web/api/variants.ts
|
|
6927
|
-
var
|
|
7234
|
+
var app23 = new Hono26().get("/:name/variants", async (c) => {
|
|
6928
7235
|
const name = c.req.param("name");
|
|
6929
7236
|
const entry = await findMind(name);
|
|
6930
7237
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -7146,19 +7453,19 @@ var app24 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
7146
7453
|
await cleanupVariant(variantName, projectRoot, variantEntry.dir, { stop: true });
|
|
7147
7454
|
return c.json({ ok: true });
|
|
7148
7455
|
});
|
|
7149
|
-
var variants_default =
|
|
7456
|
+
var variants_default = app23;
|
|
7150
7457
|
|
|
7151
7458
|
// src/web/api/volute/channels.ts
|
|
7152
|
-
import { zValidator as
|
|
7459
|
+
import { zValidator as zValidator9 } from "@hono/zod-validator";
|
|
7153
7460
|
import { Hono as Hono27 } from "hono";
|
|
7154
|
-
import { z as
|
|
7155
|
-
var createSchema2 =
|
|
7156
|
-
name:
|
|
7461
|
+
import { z as z9 } from "zod";
|
|
7462
|
+
var createSchema2 = z9.object({
|
|
7463
|
+
name: z9.string().min(1).max(50).regex(/^[a-z0-9][a-z0-9-]*$/, "Channel names must be lowercase alphanumeric with hyphens")
|
|
7157
7464
|
});
|
|
7158
|
-
var inviteSchema =
|
|
7159
|
-
username:
|
|
7465
|
+
var inviteSchema = z9.object({
|
|
7466
|
+
username: z9.string().min(1)
|
|
7160
7467
|
});
|
|
7161
|
-
var
|
|
7468
|
+
var app24 = new Hono27().get("/", async (c) => {
|
|
7162
7469
|
const user = c.get("user");
|
|
7163
7470
|
const channels = await listChannels();
|
|
7164
7471
|
const results = await Promise.all(
|
|
@@ -7169,7 +7476,7 @@ var app25 = new Hono27().get("/", async (c) => {
|
|
|
7169
7476
|
})
|
|
7170
7477
|
);
|
|
7171
7478
|
return c.json(results);
|
|
7172
|
-
}).post("/",
|
|
7479
|
+
}).post("/", zValidator9("json", createSchema2), async (c) => {
|
|
7173
7480
|
const user = c.get("user");
|
|
7174
7481
|
const body = c.req.valid("json");
|
|
7175
7482
|
try {
|
|
@@ -7208,7 +7515,7 @@ var app25 = new Hono27().get("/", async (c) => {
|
|
|
7208
7515
|
if (!ch) return c.json({ error: "Channel not found" }, 404);
|
|
7209
7516
|
const participants = await getParticipants(ch.id);
|
|
7210
7517
|
return c.json(participants);
|
|
7211
|
-
}).post("/:name/invite",
|
|
7518
|
+
}).post("/:name/invite", zValidator9("json", inviteSchema), async (c) => {
|
|
7212
7519
|
const name = c.req.param("name");
|
|
7213
7520
|
const inviter = c.get("user");
|
|
7214
7521
|
const { username } = c.req.valid("json");
|
|
@@ -7228,13 +7535,13 @@ var app25 = new Hono27().get("/", async (c) => {
|
|
|
7228
7535
|
]);
|
|
7229
7536
|
return c.json({ ok: true });
|
|
7230
7537
|
});
|
|
7231
|
-
var channels_default2 =
|
|
7538
|
+
var channels_default2 = app24;
|
|
7232
7539
|
|
|
7233
7540
|
// src/web/api/volute/chat.ts
|
|
7234
|
-
import { zValidator as
|
|
7541
|
+
import { zValidator as zValidator10 } from "@hono/zod-validator";
|
|
7235
7542
|
import { Hono as Hono28 } from "hono";
|
|
7236
|
-
import { streamSSE as
|
|
7237
|
-
import { z as
|
|
7543
|
+
import { streamSSE as streamSSE5 } from "hono/streaming";
|
|
7544
|
+
import { z as z10 } from "zod";
|
|
7238
7545
|
|
|
7239
7546
|
// src/lib/bridge-outbound.ts
|
|
7240
7547
|
function extractContent(contentBlocks) {
|
|
@@ -7308,15 +7615,15 @@ async function routeDMOutbound(conversationId, senderName, contentBlocks) {
|
|
|
7308
7615
|
}
|
|
7309
7616
|
|
|
7310
7617
|
// src/web/api/volute/chat.ts
|
|
7311
|
-
async function
|
|
7618
|
+
async function fanOutToMinds(opts) {
|
|
7312
7619
|
const participants = await getParticipants(opts.conversationId);
|
|
7313
7620
|
const mindParticipants = participants.filter(
|
|
7314
7621
|
(p) => p.userType === "mind" || p.userType === "system"
|
|
7315
7622
|
);
|
|
7316
7623
|
const participantNames = participants.map((p) => p.username);
|
|
7317
7624
|
const isDM = opts.isDM ?? participants.length === 2;
|
|
7318
|
-
const { getMindManager: getMindManager2 } = await import("./mind-manager-
|
|
7319
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
7625
|
+
const { getMindManager: getMindManager2 } = await import("./mind-manager-NBJF5D26.js");
|
|
7626
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-JTXSN7NV.js");
|
|
7320
7627
|
const manager = getMindManager2();
|
|
7321
7628
|
const sm = getSleepManagerIfReady2();
|
|
7322
7629
|
const targetMinds = mindParticipants.map((ap) => {
|
|
@@ -7352,225 +7659,131 @@ async function fanOutToMinds2(opts) {
|
|
|
7352
7659
|
});
|
|
7353
7660
|
}
|
|
7354
7661
|
}
|
|
7355
|
-
var fileSchema =
|
|
7356
|
-
filename:
|
|
7357
|
-
data:
|
|
7662
|
+
var fileSchema = z10.object({
|
|
7663
|
+
filename: z10.string(),
|
|
7664
|
+
data: z10.string()
|
|
7358
7665
|
// base64
|
|
7359
7666
|
});
|
|
7360
|
-
var chatSchema =
|
|
7361
|
-
message:
|
|
7362
|
-
conversationId:
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7667
|
+
var chatSchema = z10.object({
|
|
7668
|
+
message: z10.string().optional(),
|
|
7669
|
+
conversationId: z10.string().optional(),
|
|
7670
|
+
targetMind: z10.string().optional(),
|
|
7671
|
+
sender: z10.string().optional(),
|
|
7672
|
+
images: z10.array(
|
|
7673
|
+
z10.object({
|
|
7674
|
+
media_type: z10.string(),
|
|
7675
|
+
data: z10.string()
|
|
7368
7676
|
})
|
|
7369
7677
|
).optional(),
|
|
7370
|
-
files:
|
|
7678
|
+
files: z10.array(fileSchema).optional()
|
|
7371
7679
|
});
|
|
7372
|
-
var
|
|
7373
|
-
|
|
7374
|
-
const
|
|
7375
|
-
const
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
if (user.id !== 0 && !await isParticipantOrOwner(conversationId, user.id)) {
|
|
7387
|
-
return c.json({ error: "Conversation not found" }, 404);
|
|
7388
|
-
}
|
|
7389
|
-
} else {
|
|
7390
|
-
const participantIds = [];
|
|
7391
|
-
if (user.id !== 0) {
|
|
7392
|
-
participantIds.push(user.id);
|
|
7393
|
-
} else if (body.sender) {
|
|
7394
|
-
if (body.sender === "volute") {
|
|
7395
|
-
const systemUser = await getOrCreateSystemUser();
|
|
7396
|
-
participantIds.push(systemUser.id);
|
|
7397
|
-
} else {
|
|
7398
|
-
const senderMind = await findMind(body.sender);
|
|
7399
|
-
if (senderMind) {
|
|
7400
|
-
const senderMindUser = await getOrCreateMindUser(body.sender);
|
|
7401
|
-
participantIds.push(senderMindUser.id);
|
|
7680
|
+
var MAX_FILE_SIZE2 = 50 * 1024 * 1024;
|
|
7681
|
+
function stageFilesForMinds(files, targetMinds, senderName) {
|
|
7682
|
+
const notifications = [];
|
|
7683
|
+
for (const file of files) {
|
|
7684
|
+
const pathErr = validateFilePath(file.filename);
|
|
7685
|
+
if (pathErr)
|
|
7686
|
+
return { notifications, error: { message: `Invalid filename: ${pathErr}`, status: 400 } };
|
|
7687
|
+
const content = Buffer.from(file.data, "base64");
|
|
7688
|
+
if (content.length > MAX_FILE_SIZE2) {
|
|
7689
|
+
return {
|
|
7690
|
+
notifications,
|
|
7691
|
+
error: {
|
|
7692
|
+
message: `File too large: ${file.filename} (${formatFileSize(content.length)}, max ${formatFileSize(MAX_FILE_SIZE2)})`,
|
|
7693
|
+
status: 413
|
|
7402
7694
|
}
|
|
7403
|
-
}
|
|
7404
|
-
}
|
|
7405
|
-
participantIds.push(mindUser.id);
|
|
7406
|
-
if (participantIds.length === 2) {
|
|
7407
|
-
const existing = await findDMConversation(baseName, participantIds);
|
|
7408
|
-
if (existing) {
|
|
7409
|
-
conversationId = existing;
|
|
7410
|
-
}
|
|
7695
|
+
};
|
|
7411
7696
|
}
|
|
7412
|
-
|
|
7413
|
-
const
|
|
7414
|
-
|
|
7415
|
-
const conv2 = await createConversation(baseName, "volute", {
|
|
7416
|
-
userId: user.id !== 0 ? user.id : void 0,
|
|
7417
|
-
title,
|
|
7418
|
-
participantIds
|
|
7419
|
-
});
|
|
7420
|
-
conversationId = conv2.id;
|
|
7421
|
-
}
|
|
7422
|
-
}
|
|
7423
|
-
const conv = await getConversation(conversationId);
|
|
7424
|
-
const convTitle = conv?.title ?? null;
|
|
7425
|
-
const fileNotifications = [];
|
|
7426
|
-
if (body.files && body.files.length > 0) {
|
|
7427
|
-
const MAX_FILE_SIZE2 = 50 * 1024 * 1024;
|
|
7428
|
-
for (const file of body.files) {
|
|
7429
|
-
const pathErr = validateFilePath(file.filename);
|
|
7430
|
-
if (pathErr) return c.json({ error: `Invalid filename: ${pathErr}` }, 400);
|
|
7431
|
-
const content = Buffer.from(file.data, "base64");
|
|
7432
|
-
if (content.length > MAX_FILE_SIZE2) {
|
|
7433
|
-
return c.json(
|
|
7434
|
-
{
|
|
7435
|
-
error: `File too large: ${file.filename} (${formatFileSize(content.length)}, max ${formatFileSize(MAX_FILE_SIZE2)})`
|
|
7436
|
-
},
|
|
7437
|
-
413
|
|
7438
|
-
);
|
|
7439
|
-
}
|
|
7440
|
-
const { id } = stageFile(baseName, senderName, file.filename, content, file.filename);
|
|
7441
|
-
fileNotifications.push(
|
|
7697
|
+
for (const mind of targetMinds) {
|
|
7698
|
+
const { id } = stageFile(mind, senderName, file.filename, content, file.filename);
|
|
7699
|
+
notifications.push(
|
|
7442
7700
|
`[file] ${senderName} sent ${file.filename} (${formatFileSize(content.length)}) \u2014 run: volute chat accept ${id}`
|
|
7443
7701
|
);
|
|
7444
7702
|
}
|
|
7445
7703
|
}
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
contentBlocks.push({ type: "text", text: body.message });
|
|
7449
|
-
}
|
|
7450
|
-
for (const note of fileNotifications) {
|
|
7451
|
-
contentBlocks.push({ type: "text", text: note });
|
|
7452
|
-
}
|
|
7453
|
-
if (body.images) {
|
|
7454
|
-
for (const img of body.images) {
|
|
7455
|
-
contentBlocks.push({ type: "image", media_type: img.media_type, data: img.data });
|
|
7456
|
-
}
|
|
7457
|
-
}
|
|
7458
|
-
const senderIsMind = user.id === 0 && body.sender && await findMind(body.sender);
|
|
7459
|
-
const mindSession = c.get("mindSession");
|
|
7460
|
-
let sourceEventId;
|
|
7461
|
-
let turnId;
|
|
7462
|
-
if (senderIsMind) {
|
|
7463
|
-
sourceEventId = getLastToolUseEventId(body.sender, mindSession);
|
|
7464
|
-
turnId = getActiveTurnId(body.sender, mindSession);
|
|
7465
|
-
}
|
|
7466
|
-
await addMessage(conversationId, "user", senderName, contentBlocks, {
|
|
7467
|
-
sourceEventId,
|
|
7468
|
-
turnId
|
|
7469
|
-
});
|
|
7470
|
-
if (senderIsMind) {
|
|
7471
|
-
routeOutboundBridge(conversationId, senderName, contentBlocks).catch((err) => {
|
|
7472
|
-
logger_default.warn("outbound bridge routing failed", logger_default.errorData(err));
|
|
7473
|
-
});
|
|
7474
|
-
}
|
|
7475
|
-
const isDM = conv?.type === "dm";
|
|
7476
|
-
await fanOutToMinds2({
|
|
7477
|
-
conversationId,
|
|
7478
|
-
contentBlocks,
|
|
7479
|
-
senderName,
|
|
7480
|
-
convTitle,
|
|
7481
|
-
isDM,
|
|
7482
|
-
slugExtra: conv ? { convType: conv.type, convName: conv.name } : void 0,
|
|
7483
|
-
targetName: (username) => username === baseName ? name : username
|
|
7484
|
-
});
|
|
7485
|
-
if (senderIsMind && body.message) {
|
|
7486
|
-
const participants = await getParticipants(conversationId);
|
|
7487
|
-
const hasSystemUser = participants.some((p) => p.userType === "system");
|
|
7488
|
-
if (hasSystemUser) {
|
|
7489
|
-
generateSystemReply(conversationId, baseName, body.message).catch(
|
|
7490
|
-
(err) => logger_default.error(`system reply generation failed for ${baseName}`, logger_default.errorData(err))
|
|
7491
|
-
);
|
|
7492
|
-
}
|
|
7493
|
-
}
|
|
7494
|
-
return c.json({ ok: true, conversationId });
|
|
7495
|
-
}).get("/:name/conversations/:id/events", async (c) => {
|
|
7496
|
-
const conversationId = c.req.param("id");
|
|
7497
|
-
const user = c.get("user");
|
|
7498
|
-
if (user.id !== 0 && !await isParticipantOrOwner(conversationId, user.id)) {
|
|
7499
|
-
return c.json({ error: "Conversation not found" }, 404);
|
|
7500
|
-
}
|
|
7501
|
-
return streamSSE6(c, async (stream) => {
|
|
7502
|
-
const unsubscribe = subscribe2(conversationId, (event) => {
|
|
7503
|
-
stream.writeSSE({ data: JSON.stringify(event) }).catch((err) => {
|
|
7504
|
-
if (!stream.aborted) console.error("[chat] SSE write error:", err);
|
|
7505
|
-
});
|
|
7506
|
-
});
|
|
7507
|
-
const keepAlive = setInterval(() => {
|
|
7508
|
-
stream.writeSSE({ data: "" }).catch((err) => {
|
|
7509
|
-
if (!stream.aborted) console.error("[chat] SSE ping error:", err);
|
|
7510
|
-
});
|
|
7511
|
-
}, 15e3);
|
|
7512
|
-
await new Promise((resolve20) => {
|
|
7513
|
-
stream.onAbort(() => {
|
|
7514
|
-
unsubscribe();
|
|
7515
|
-
clearInterval(keepAlive);
|
|
7516
|
-
resolve20();
|
|
7517
|
-
});
|
|
7518
|
-
});
|
|
7519
|
-
});
|
|
7520
|
-
});
|
|
7521
|
-
var unifiedChatSchema2 = z11.object({
|
|
7522
|
-
message: z11.string().optional(),
|
|
7523
|
-
conversationId: z11.string(),
|
|
7524
|
-
images: z11.array(z11.object({ media_type: z11.string(), data: z11.string() })).optional(),
|
|
7525
|
-
files: z11.array(fileSchema).optional()
|
|
7526
|
-
});
|
|
7704
|
+
return { notifications };
|
|
7705
|
+
}
|
|
7527
7706
|
var unifiedChatApp = new Hono28().post(
|
|
7528
7707
|
"/chat",
|
|
7529
|
-
|
|
7708
|
+
zValidator10("json", chatSchema),
|
|
7530
7709
|
async (c) => {
|
|
7531
7710
|
const user = c.get("user");
|
|
7532
7711
|
const body = c.req.valid("json");
|
|
7533
7712
|
if (!body.message && (!body.images || body.images.length === 0) && (!body.files || body.files.length === 0)) {
|
|
7534
7713
|
return c.json({ error: "message, images, or files required" }, 400);
|
|
7535
7714
|
}
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7715
|
+
if (!body.conversationId && !body.targetMind) {
|
|
7716
|
+
return c.json({ error: "conversationId or targetMind required" }, 400);
|
|
7717
|
+
}
|
|
7718
|
+
const senderName = user.id === 0 && body.sender ? body.sender : user.username;
|
|
7719
|
+
const senderIsMind = user.id === 0 && body.sender && await findMind(body.sender) || user.user_type === "mind";
|
|
7720
|
+
let baseName;
|
|
7721
|
+
let variantName;
|
|
7722
|
+
let conversationId = body.conversationId;
|
|
7723
|
+
if (body.targetMind) {
|
|
7724
|
+
variantName = body.targetMind;
|
|
7725
|
+
baseName = await getBaseName(body.targetMind);
|
|
7726
|
+
const entry = await findMind(baseName);
|
|
7727
|
+
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
7728
|
+
if (conversationId) {
|
|
7729
|
+
if (user.id !== 0 && !await isParticipantOrOwner(conversationId, user.id)) {
|
|
7730
|
+
return c.json({ error: "Conversation not found" }, 404);
|
|
7731
|
+
}
|
|
7732
|
+
} else {
|
|
7733
|
+
const mindUser = await getOrCreateMindUser(baseName);
|
|
7734
|
+
const participantIds = [];
|
|
7735
|
+
if (user.id !== 0) {
|
|
7736
|
+
participantIds.push(user.id);
|
|
7737
|
+
} else if (body.sender) {
|
|
7738
|
+
if (body.sender === "volute") {
|
|
7739
|
+
const systemUser = await getOrCreateSystemUser();
|
|
7740
|
+
participantIds.push(systemUser.id);
|
|
7741
|
+
} else {
|
|
7742
|
+
const senderMind = await findMind(body.sender);
|
|
7743
|
+
if (senderMind) {
|
|
7744
|
+
const senderMindUser = await getOrCreateMindUser(body.sender);
|
|
7745
|
+
participantIds.push(senderMindUser.id);
|
|
7746
|
+
}
|
|
7747
|
+
}
|
|
7748
|
+
}
|
|
7749
|
+
participantIds.push(mindUser.id);
|
|
7750
|
+
if (participantIds.length === 2) {
|
|
7751
|
+
const existing = await findDMConversation(baseName, participantIds);
|
|
7752
|
+
if (existing) {
|
|
7753
|
+
conversationId = existing;
|
|
7754
|
+
}
|
|
7755
|
+
}
|
|
7756
|
+
if (!conversationId) {
|
|
7757
|
+
const participantNames = /* @__PURE__ */ new Set([senderName, baseName]);
|
|
7758
|
+
const title = [...participantNames].join(", ");
|
|
7759
|
+
const conv2 = await createConversation(baseName, "volute", {
|
|
7760
|
+
userId: user.id !== 0 ? user.id : void 0,
|
|
7761
|
+
title,
|
|
7762
|
+
participantIds
|
|
7763
|
+
});
|
|
7764
|
+
conversationId = conv2.id;
|
|
7765
|
+
}
|
|
7766
|
+
}
|
|
7767
|
+
} else {
|
|
7768
|
+
if (user.id !== 0 && !await isParticipantOrOwner(conversationId, user.id)) {
|
|
7769
|
+
return c.json({ error: "Conversation not found" }, 404);
|
|
7770
|
+
}
|
|
7540
7771
|
}
|
|
7541
|
-
const
|
|
7772
|
+
const conv = await getConversation(conversationId);
|
|
7773
|
+
if (!conv) return c.json({ error: "Conversation not found" }, 404);
|
|
7774
|
+
const convTitle = conv.title;
|
|
7542
7775
|
const fileNotifications = [];
|
|
7543
7776
|
if (body.files && body.files.length > 0) {
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
const pathErr = validateFilePath(file.filename);
|
|
7551
|
-
if (pathErr) return c.json({ error: `Invalid filename: ${pathErr}` }, 400);
|
|
7552
|
-
const content = Buffer.from(file.data, "base64");
|
|
7553
|
-
if (content.length > MAX_FILE_SIZE2) {
|
|
7554
|
-
return c.json(
|
|
7555
|
-
{
|
|
7556
|
-
error: `File too large: ${file.filename} (${formatFileSize(content.length)}, max ${formatFileSize(MAX_FILE_SIZE2)})`
|
|
7557
|
-
},
|
|
7558
|
-
413
|
|
7559
|
-
);
|
|
7560
|
-
}
|
|
7561
|
-
for (const mind of mindParticipants) {
|
|
7562
|
-
const { id } = stageFile(
|
|
7563
|
-
mind.username,
|
|
7564
|
-
senderName,
|
|
7565
|
-
file.filename,
|
|
7566
|
-
content,
|
|
7567
|
-
file.filename
|
|
7568
|
-
);
|
|
7569
|
-
fileNotifications.push(
|
|
7570
|
-
`[file] ${senderName} sent ${file.filename} (${formatFileSize(content.length)}) \u2014 run: volute chat accept ${id}`
|
|
7571
|
-
);
|
|
7572
|
-
}
|
|
7777
|
+
let fileTargets;
|
|
7778
|
+
if (baseName) {
|
|
7779
|
+
fileTargets = [baseName];
|
|
7780
|
+
} else {
|
|
7781
|
+
const participants = await getParticipants(conversationId);
|
|
7782
|
+
fileTargets = participants.filter((p) => p.userType === "mind" && p.username !== senderName).map((p) => p.username);
|
|
7573
7783
|
}
|
|
7784
|
+
const { notifications, error } = stageFilesForMinds(body.files, fileTargets, senderName);
|
|
7785
|
+
if (error) return c.json({ error: error.message }, error.status);
|
|
7786
|
+
fileNotifications.push(...notifications);
|
|
7574
7787
|
}
|
|
7575
7788
|
const contentBlocks = [];
|
|
7576
7789
|
if (body.message) contentBlocks.push({ type: "text", text: body.message });
|
|
@@ -7582,55 +7795,90 @@ var unifiedChatApp = new Hono28().post(
|
|
|
7582
7795
|
contentBlocks.push({ type: "image", media_type: img.media_type, data: img.data });
|
|
7583
7796
|
}
|
|
7584
7797
|
}
|
|
7585
|
-
const
|
|
7586
|
-
let
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
unifiedSourceEventId = getLastToolUseEventId(senderName, unifiedMindSession);
|
|
7590
|
-
unifiedTurnId = getActiveTurnId(senderName, unifiedMindSession);
|
|
7591
|
-
}
|
|
7592
|
-
await addMessage(body.conversationId, "user", senderName, contentBlocks, {
|
|
7593
|
-
sourceEventId: unifiedSourceEventId,
|
|
7594
|
-
turnId: unifiedTurnId
|
|
7595
|
-
});
|
|
7596
|
-
if (user.user_type === "mind") {
|
|
7597
|
-
routeOutboundBridge(body.conversationId, senderName, contentBlocks).catch((err) => {
|
|
7798
|
+
const message = await addMessage(conversationId, "user", senderName, contentBlocks);
|
|
7799
|
+
let outboundId;
|
|
7800
|
+
if (senderIsMind) {
|
|
7801
|
+
routeOutboundBridge(conversationId, senderName, contentBlocks).catch((err) => {
|
|
7598
7802
|
logger_default.warn("outbound bridge routing failed", logger_default.errorData(err));
|
|
7599
7803
|
});
|
|
7804
|
+
const channel = buildVoluteSlug({
|
|
7805
|
+
participants: await getParticipants(conversationId),
|
|
7806
|
+
mindUsername: senderName,
|
|
7807
|
+
convTitle,
|
|
7808
|
+
conversationId,
|
|
7809
|
+
convType: conv.type,
|
|
7810
|
+
convName: conv.name
|
|
7811
|
+
});
|
|
7812
|
+
try {
|
|
7813
|
+
outboundId = await recordOutbound(senderName, channel, extractTextContent(contentBlocks), {
|
|
7814
|
+
messageId: message?.id != null ? String(message.id) : void 0
|
|
7815
|
+
});
|
|
7816
|
+
} catch (err) {
|
|
7817
|
+
logger_default.warn(`recordOutbound failed for ${senderName}`, logger_default.errorData(err));
|
|
7818
|
+
}
|
|
7600
7819
|
}
|
|
7601
7820
|
const isDM = conv.type === "dm";
|
|
7602
|
-
await
|
|
7603
|
-
conversationId
|
|
7821
|
+
await fanOutToMinds({
|
|
7822
|
+
conversationId,
|
|
7604
7823
|
contentBlocks,
|
|
7605
7824
|
senderName,
|
|
7606
|
-
convTitle
|
|
7825
|
+
convTitle,
|
|
7607
7826
|
isDM,
|
|
7608
|
-
slugExtra: { convType: conv.type, convName: conv.name }
|
|
7827
|
+
slugExtra: { convType: conv.type, convName: conv.name },
|
|
7828
|
+
// Variant-aware targeting: when targetMind is a variant, route to the variant name
|
|
7829
|
+
targetName: baseName ? (username) => username === baseName ? variantName : username : void 0
|
|
7609
7830
|
});
|
|
7610
|
-
|
|
7611
|
-
|
|
7831
|
+
const systemReplyTarget = baseName ?? senderName;
|
|
7832
|
+
if (senderIsMind && body.message) {
|
|
7833
|
+
const participants = await getParticipants(conversationId);
|
|
7612
7834
|
const hasSystemUser = participants.some((p) => p.userType === "system");
|
|
7613
7835
|
if (hasSystemUser) {
|
|
7614
|
-
generateSystemReply(
|
|
7615
|
-
(err) => logger_default.error(`system reply generation failed for ${
|
|
7836
|
+
generateSystemReply(conversationId, systemReplyTarget, body.message).catch(
|
|
7837
|
+
(err) => logger_default.error(`system reply generation failed for ${systemReplyTarget}`, logger_default.errorData(err))
|
|
7616
7838
|
);
|
|
7617
7839
|
}
|
|
7618
7840
|
}
|
|
7619
|
-
return c.json({ ok: true, conversationId
|
|
7841
|
+
return c.json({ ok: true, conversationId, outboundId });
|
|
7620
7842
|
}
|
|
7621
7843
|
);
|
|
7622
|
-
var
|
|
7844
|
+
var app25 = new Hono28().get("/:name/conversations/:id/events", async (c) => {
|
|
7845
|
+
const conversationId = c.req.param("id");
|
|
7846
|
+
const user = c.get("user");
|
|
7847
|
+
if (user.id !== 0 && !await isParticipantOrOwner(conversationId, user.id)) {
|
|
7848
|
+
return c.json({ error: "Conversation not found" }, 404);
|
|
7849
|
+
}
|
|
7850
|
+
return streamSSE5(c, async (stream) => {
|
|
7851
|
+
const unsubscribe = subscribe2(conversationId, (event) => {
|
|
7852
|
+
stream.writeSSE({ data: JSON.stringify(event) }).catch((err) => {
|
|
7853
|
+
if (!stream.aborted) console.error("[chat] SSE write error:", err);
|
|
7854
|
+
});
|
|
7855
|
+
});
|
|
7856
|
+
const keepAlive = setInterval(() => {
|
|
7857
|
+
stream.writeSSE({ data: "" }).catch((err) => {
|
|
7858
|
+
if (!stream.aborted) console.error("[chat] SSE ping error:", err);
|
|
7859
|
+
});
|
|
7860
|
+
}, 15e3);
|
|
7861
|
+
await new Promise((resolve20) => {
|
|
7862
|
+
stream.onAbort(() => {
|
|
7863
|
+
unsubscribe();
|
|
7864
|
+
clearInterval(keepAlive);
|
|
7865
|
+
resolve20();
|
|
7866
|
+
});
|
|
7867
|
+
});
|
|
7868
|
+
});
|
|
7869
|
+
});
|
|
7870
|
+
var chat_default = app25;
|
|
7623
7871
|
|
|
7624
7872
|
// src/web/api/volute/conversations.ts
|
|
7625
|
-
import { zValidator as
|
|
7873
|
+
import { zValidator as zValidator11 } from "@hono/zod-validator";
|
|
7626
7874
|
import { Hono as Hono29 } from "hono";
|
|
7627
|
-
import { z as
|
|
7628
|
-
var createConvSchema =
|
|
7629
|
-
title:
|
|
7630
|
-
participantIds:
|
|
7631
|
-
participantNames:
|
|
7875
|
+
import { z as z11 } from "zod";
|
|
7876
|
+
var createConvSchema = z11.object({
|
|
7877
|
+
title: z11.string().optional(),
|
|
7878
|
+
participantIds: z11.array(z11.number()).optional(),
|
|
7879
|
+
participantNames: z11.array(z11.string()).optional()
|
|
7632
7880
|
});
|
|
7633
|
-
var
|
|
7881
|
+
var app26 = new Hono29().get("/:name/conversations", async (c) => {
|
|
7634
7882
|
const name = c.req.param("name");
|
|
7635
7883
|
const user = c.get("user");
|
|
7636
7884
|
let lookupId = user.id;
|
|
@@ -7641,7 +7889,7 @@ var app27 = new Hono29().get("/:name/conversations", async (c) => {
|
|
|
7641
7889
|
const all = await listConversationsForUser(lookupId);
|
|
7642
7890
|
const convs = all.filter((c2) => c2.mind_name === name || c2.type === "channel");
|
|
7643
7891
|
return c.json(convs);
|
|
7644
|
-
}).post("/:name/conversations",
|
|
7892
|
+
}).post("/:name/conversations", zValidator11("json", createConvSchema), async (c) => {
|
|
7645
7893
|
const name = c.req.param("name");
|
|
7646
7894
|
const user = c.get("user");
|
|
7647
7895
|
const body = c.req.valid("json");
|
|
@@ -7738,109 +7986,12 @@ var app27 = new Hono29().get("/:name/conversations", async (c) => {
|
|
|
7738
7986
|
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
7739
7987
|
return c.json({ ok: true });
|
|
7740
7988
|
});
|
|
7741
|
-
var conversations_default2 =
|
|
7742
|
-
|
|
7743
|
-
// src/web/api/volute/user-conversations.ts
|
|
7744
|
-
import { zValidator as zValidator13 } from "@hono/zod-validator";
|
|
7745
|
-
import { Hono as Hono30 } from "hono";
|
|
7746
|
-
import { streamSSE as streamSSE7 } from "hono/streaming";
|
|
7747
|
-
import { z as z13 } from "zod";
|
|
7748
|
-
var createSchema3 = z13.object({
|
|
7749
|
-
title: z13.string().optional(),
|
|
7750
|
-
participantNames: z13.array(z13.string()).min(1)
|
|
7751
|
-
});
|
|
7752
|
-
var app28 = new Hono30().use("*", authMiddleware).get("/", async (c) => {
|
|
7753
|
-
const user = c.get("user");
|
|
7754
|
-
const convs = await listConversationsWithParticipants(user.id);
|
|
7755
|
-
return c.json(convs);
|
|
7756
|
-
}).get("/:id/messages", async (c) => {
|
|
7757
|
-
const id = c.req.param("id");
|
|
7758
|
-
const user = c.get("user");
|
|
7759
|
-
if (user.id !== 0 && !await isParticipantOrOwner(id, user.id)) {
|
|
7760
|
-
return c.json({ error: "Conversation not found" }, 404);
|
|
7761
|
-
}
|
|
7762
|
-
const msgs = await getMessages(id);
|
|
7763
|
-
return c.json(msgs);
|
|
7764
|
-
}).post("/", zValidator13("json", createSchema3), async (c) => {
|
|
7765
|
-
const user = c.get("user");
|
|
7766
|
-
const body = c.req.valid("json");
|
|
7767
|
-
const participantIds = /* @__PURE__ */ new Set();
|
|
7768
|
-
if (user.id !== 0) participantIds.add(user.id);
|
|
7769
|
-
let firstMindName;
|
|
7770
|
-
for (const name of body.participantNames) {
|
|
7771
|
-
const existing = await getUserByUsername(name);
|
|
7772
|
-
if (existing) {
|
|
7773
|
-
participantIds.add(existing.id);
|
|
7774
|
-
if (!firstMindName && existing.user_type === "mind") firstMindName = name;
|
|
7775
|
-
continue;
|
|
7776
|
-
}
|
|
7777
|
-
if (await findMind(name)) {
|
|
7778
|
-
const au = await getOrCreateMindUser(name);
|
|
7779
|
-
participantIds.add(au.id);
|
|
7780
|
-
if (!firstMindName) firstMindName = name;
|
|
7781
|
-
continue;
|
|
7782
|
-
}
|
|
7783
|
-
return c.json({ error: `User not found: ${name}` }, 400);
|
|
7784
|
-
}
|
|
7785
|
-
if (!firstMindName) {
|
|
7786
|
-
return c.json({ error: "At least one mind participant is required" }, 400);
|
|
7787
|
-
}
|
|
7788
|
-
if (participantIds.size > 2) {
|
|
7789
|
-
return c.json({ error: "Use channels for multi-participant conversations" }, 400);
|
|
7790
|
-
}
|
|
7791
|
-
const conv = await createConversation(firstMindName, "volute", {
|
|
7792
|
-
userId: user.id !== 0 ? user.id : void 0,
|
|
7793
|
-
title: body.title,
|
|
7794
|
-
participantIds: [...participantIds]
|
|
7795
|
-
});
|
|
7796
|
-
return c.json(conv, 201);
|
|
7797
|
-
}).get("/:id/events", async (c) => {
|
|
7798
|
-
const conversationId = c.req.param("id");
|
|
7799
|
-
const user = c.get("user");
|
|
7800
|
-
if (user.id !== 0 && !await isParticipantOrOwner(conversationId, user.id)) {
|
|
7801
|
-
return c.json({ error: "Conversation not found" }, 404);
|
|
7802
|
-
}
|
|
7803
|
-
return streamSSE7(c, async (stream) => {
|
|
7804
|
-
const unsubscribe = subscribe2(conversationId, (event) => {
|
|
7805
|
-
stream.writeSSE({ data: JSON.stringify(event) }).catch((err) => {
|
|
7806
|
-
if (!stream.aborted) console.error("[chat] SSE write error:", err);
|
|
7807
|
-
});
|
|
7808
|
-
});
|
|
7809
|
-
const keepAlive = setInterval(() => {
|
|
7810
|
-
stream.writeSSE({ data: "" }).catch((err) => {
|
|
7811
|
-
if (!stream.aborted) console.error("[chat] SSE ping error:", err);
|
|
7812
|
-
});
|
|
7813
|
-
}, 15e3);
|
|
7814
|
-
await new Promise((resolve20) => {
|
|
7815
|
-
stream.onAbort(() => {
|
|
7816
|
-
unsubscribe();
|
|
7817
|
-
clearInterval(keepAlive);
|
|
7818
|
-
resolve20();
|
|
7819
|
-
});
|
|
7820
|
-
});
|
|
7821
|
-
});
|
|
7822
|
-
}).put("/:id/private", zValidator13("json", z13.object({ private: z13.boolean() })), async (c) => {
|
|
7823
|
-
const id = c.req.param("id");
|
|
7824
|
-
const user = c.get("user");
|
|
7825
|
-
if (!await isParticipantOrOwner(id, user.id)) {
|
|
7826
|
-
return c.json({ error: "Forbidden" }, 403);
|
|
7827
|
-
}
|
|
7828
|
-
const body = c.req.valid("json");
|
|
7829
|
-
await setConversationPrivate(id, body.private);
|
|
7830
|
-
return c.json({ ok: true });
|
|
7831
|
-
}).delete("/:id", async (c) => {
|
|
7832
|
-
const id = c.req.param("id");
|
|
7833
|
-
const user = c.get("user");
|
|
7834
|
-
const deleted = await deleteConversationForUser(id, user.id);
|
|
7835
|
-
if (!deleted) return c.json({ error: "Conversation not found" }, 404);
|
|
7836
|
-
return c.json({ ok: true });
|
|
7837
|
-
});
|
|
7838
|
-
var user_conversations_default = app28;
|
|
7989
|
+
var conversations_default2 = app26;
|
|
7839
7990
|
|
|
7840
7991
|
// src/web/app.ts
|
|
7841
7992
|
var httpLog = logger_default.child("http");
|
|
7842
|
-
var
|
|
7843
|
-
|
|
7993
|
+
var app27 = new Hono30();
|
|
7994
|
+
app27.onError((err, c) => {
|
|
7844
7995
|
if (err instanceof HTTPException) {
|
|
7845
7996
|
return err.getResponse();
|
|
7846
7997
|
}
|
|
@@ -7851,10 +8002,10 @@ app29.onError((err, c) => {
|
|
|
7851
8002
|
});
|
|
7852
8003
|
return c.json({ error: "Internal server error" }, 500);
|
|
7853
8004
|
});
|
|
7854
|
-
|
|
8005
|
+
app27.notFound((c) => {
|
|
7855
8006
|
return c.json({ error: "Not found" }, 404);
|
|
7856
8007
|
});
|
|
7857
|
-
|
|
8008
|
+
app27.use("*", async (c, next) => {
|
|
7858
8009
|
const start = Date.now();
|
|
7859
8010
|
await next();
|
|
7860
8011
|
const duration = Date.now() - start;
|
|
@@ -7865,7 +8016,7 @@ app29.use("*", async (c, next) => {
|
|
|
7865
8016
|
httpLog.debug("request", data);
|
|
7866
8017
|
}
|
|
7867
8018
|
});
|
|
7868
|
-
|
|
8019
|
+
app27.get("/api/health", (c) => {
|
|
7869
8020
|
let version = "unknown";
|
|
7870
8021
|
let cached = null;
|
|
7871
8022
|
try {
|
|
@@ -7880,39 +8031,39 @@ app29.get("/api/health", (c) => {
|
|
|
7880
8031
|
...cached?.updateAvailable ? { updateAvailable: true, latest: cached.latest } : {}
|
|
7881
8032
|
});
|
|
7882
8033
|
});
|
|
7883
|
-
|
|
7884
|
-
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
7890
|
-
|
|
7891
|
-
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
7898
|
-
|
|
7899
|
-
|
|
7900
|
-
|
|
7901
|
-
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
var app_default =
|
|
8034
|
+
app27.use("/api/*", bodyLimit({ maxSize: 10 * 1024 * 1024 }));
|
|
8035
|
+
app27.use("/api/*", csrf());
|
|
8036
|
+
app27.use("/api/activity/*", authMiddleware);
|
|
8037
|
+
app27.use("/api/minds/*", authMiddleware);
|
|
8038
|
+
app27.use("/api/conversations/*", authMiddleware);
|
|
8039
|
+
app27.use("/api/system/*", authMiddleware);
|
|
8040
|
+
app27.use("/api/env/*", authMiddleware);
|
|
8041
|
+
app27.use("/api/prompts/*", authMiddleware);
|
|
8042
|
+
app27.use("/api/skills/*", authMiddleware);
|
|
8043
|
+
app27.use("/api/extensions/*", authMiddleware);
|
|
8044
|
+
app27.use("/api/bridges/*", authMiddleware);
|
|
8045
|
+
app27.use("/api/config/*", authMiddleware);
|
|
8046
|
+
app27.use("/api/v1/*", authMiddleware);
|
|
8047
|
+
app27.route("/api/setup", setup_default);
|
|
8048
|
+
app27.route("/public", public_files_default);
|
|
8049
|
+
app27.route("/api/config", config_default);
|
|
8050
|
+
var routes = app27.route("/api/activity", activity_default).route("/api/keys", keys_default).route("/api/auth", auth_default).route("/api/system", system_default).route("/api/system", update_default).route("/api/minds", minds_default).route("/api/minds", chat_default).route("/api/minds", schedules_default).route("/api/minds", logs_default).route("/api/minds", typing_default).route("/api/minds", variants_default).route("/api/minds", file_sharing_default).route("/api/minds", files_default).route("/api/minds", channels_default).route("/api/minds", shared_default).route("/api/minds", env_default).route("/api/minds", mind_skills_default).route("/api/minds", conversations_default2).route("/api/env", sharedEnvApp).route("/api/prompts", prompts_default).route("/api/skills", skills_default).route("/api/bridges", bridges_default).route("/api/extensions", extensions_default).route("/api/v1/conversations", conversations_default).route("/api/v1/events", events_default).route("/api/v1", unifiedChatApp).route("/api/v1/channels", channels_default2).route("/api/v1/history", history_default);
|
|
8051
|
+
app27.route("/api/v1/minds", minds_default);
|
|
8052
|
+
app27.route("/api/v1/minds", chat_default);
|
|
8053
|
+
app27.route("/api/v1/minds", typing_default);
|
|
8054
|
+
app27.route("/api/v1/minds", variants_default);
|
|
8055
|
+
app27.route("/api/v1/minds", files_default);
|
|
8056
|
+
app27.route("/api/v1/minds", env_default);
|
|
8057
|
+
app27.route("/api/v1/minds", mind_skills_default);
|
|
8058
|
+
app27.route("/api/v1/minds", schedules_default);
|
|
8059
|
+
app27.route("/api/v1/minds", logs_default);
|
|
8060
|
+
app27.route("/api/v1/system", system_default);
|
|
8061
|
+
app27.route("/api/v1/system", update_default);
|
|
8062
|
+
app27.route("/api/v1/prompts", prompts_default);
|
|
8063
|
+
app27.route("/api/v1/skills", skills_default);
|
|
8064
|
+
app27.route("/api/v1/env", sharedEnvApp);
|
|
8065
|
+
app27.route("/api/conversations", conversations_default);
|
|
8066
|
+
var app_default = app27;
|
|
7916
8067
|
|
|
7917
8068
|
// src/web/server.ts
|
|
7918
8069
|
import { existsSync as existsSync12 } from "fs";
|
|
@@ -8044,18 +8195,18 @@ async function startDaemon(opts) {
|
|
|
8044
8195
|
} catch (err) {
|
|
8045
8196
|
logger_default.warn("failed to initialize shared repo", logger_default.errorData(err));
|
|
8046
8197
|
}
|
|
8047
|
-
const { migrateSetupCompleted } = await import("./setup-
|
|
8198
|
+
const { migrateSetupCompleted } = await import("./setup-TISPCO22.js");
|
|
8048
8199
|
migrateSetupCompleted();
|
|
8049
8200
|
await (await import("./db-F34YLV7D.js")).getDb();
|
|
8050
8201
|
try {
|
|
8051
|
-
const { eq:
|
|
8202
|
+
const { eq: eq8, and: and6 } = await import("drizzle-orm");
|
|
8052
8203
|
const { users: users2 } = await import("./schema-PA3M5ZKH.js");
|
|
8053
8204
|
const db = await (await import("./db-F34YLV7D.js")).getDb();
|
|
8054
|
-
await db.update(users2).set({ role: "system" }).where(
|
|
8205
|
+
await db.update(users2).set({ role: "system" }).where(and6(eq8(users2.user_type, "system"), eq8(users2.role, "user")));
|
|
8055
8206
|
} catch (err) {
|
|
8056
8207
|
logger_default.warn("failed to migrate system user role", logger_default.errorData(err));
|
|
8057
8208
|
}
|
|
8058
|
-
const { initSandbox } = await import("./sandbox-
|
|
8209
|
+
const { initSandbox } = await import("./sandbox-GJOK4QLQ.js");
|
|
8059
8210
|
await initSandbox();
|
|
8060
8211
|
try {
|
|
8061
8212
|
await syncBuiltinSkills();
|
|
@@ -8075,7 +8226,7 @@ async function startDaemon(opts) {
|
|
|
8075
8226
|
logger_default.warn("failed to ensure #system channel", logger_default.errorData(err));
|
|
8076
8227
|
}
|
|
8077
8228
|
try {
|
|
8078
|
-
const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-
|
|
8229
|
+
const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-GKCDSO4T.js");
|
|
8079
8230
|
await getOrCreateSystemUser2();
|
|
8080
8231
|
} catch (err) {
|
|
8081
8232
|
logger_default.warn(
|
|
@@ -8156,10 +8307,10 @@ async function startDaemon(opts) {
|
|
|
8156
8307
|
await Promise.all(workers);
|
|
8157
8308
|
}
|
|
8158
8309
|
try {
|
|
8159
|
-
const { isSetupComplete: isSetupComplete2 } = await import("./setup-
|
|
8310
|
+
const { isSetupComplete: isSetupComplete2 } = await import("./setup-TISPCO22.js");
|
|
8160
8311
|
if (isSetupComplete2()) {
|
|
8161
|
-
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-
|
|
8162
|
-
const { startSpiritFull } = await import("./mind-service-
|
|
8312
|
+
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-VRONKFMF.js");
|
|
8313
|
+
const { startSpiritFull } = await import("./mind-service-2MQ6UK5N.js");
|
|
8163
8314
|
await ensureSpiritProject();
|
|
8164
8315
|
await syncSpiritTemplate();
|
|
8165
8316
|
const spiritEntry = await findMind("volute");
|
|
@@ -8176,7 +8327,7 @@ async function startDaemon(opts) {
|
|
|
8176
8327
|
bridgeManager.startBridges(daemonPort).catch((err) => {
|
|
8177
8328
|
logger_default.warn("failed to start bridges", logger_default.errorData(err));
|
|
8178
8329
|
});
|
|
8179
|
-
import("./cloud-sync-
|
|
8330
|
+
import("./cloud-sync-4NWLMFVH.js").then(
|
|
8180
8331
|
({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
|
|
8181
8332
|
logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
|
|
8182
8333
|
})
|
|
@@ -8184,7 +8335,7 @@ async function startDaemon(opts) {
|
|
|
8184
8335
|
logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
|
|
8185
8336
|
});
|
|
8186
8337
|
try {
|
|
8187
|
-
const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-
|
|
8338
|
+
const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-NBI2MTJO.js");
|
|
8188
8339
|
backfillTemplateHashes();
|
|
8189
8340
|
notifyVersionUpdate().catch((err) => {
|
|
8190
8341
|
logger_default.warn("failed to send version update notifications", logger_default.errorData(err));
|