volute 0.51.0 → 0.53.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/dist/{accept-5NK5XJB5.js → accept-I4GVGWA3.js} +6 -3
- package/dist/{auth-X6EMNEGB.js → auth-KRDSM55Q.js} +3 -1
- package/dist/{chat-VAZ25KVT.js → chat-WK2BLM7B.js} +9 -9
- package/dist/{chunk-VAEZCIJC.js → chunk-23UJGVYP.js} +9 -9
- package/dist/{chunk-VSBMLB2W.js → chunk-5DBGQBQR.js} +138 -13
- package/dist/chunk-5RBEEAKW.js +58 -0
- package/dist/{chunk-BSHZ23MS.js → chunk-7ARONCZW.js} +25 -3
- package/dist/{chunk-HKP6OAJP.js → chunk-7LT3YZUR.js} +12 -0
- package/dist/{chunk-FXWBFDQR.js → chunk-AP2KQMMW.js} +48 -9
- package/dist/{chunk-DMK5ASKV.js → chunk-F6D766VQ.js} +6 -2
- package/dist/{chunk-VP5PFNZO.js → chunk-HASADR3E.js} +73 -20
- package/dist/{chunk-ICL6T4SN.js → chunk-IQ7VNWH6.js} +26 -2
- package/dist/{chunk-G7A5LEVI.js → chunk-LN7XJ56R.js} +6 -6
- package/dist/{chunk-DN3LKLYR.js → chunk-P2TBUKLW.js} +3 -3
- package/dist/{chunk-Q7XQLKZB.js → chunk-PIZDPY4G.js} +20 -3
- package/dist/{chunk-JHKBALS2.js → chunk-RMADJFA7.js} +1 -1
- package/dist/{chunk-7HN63Q7J.js → chunk-RQRO6PC7.js} +40 -6
- package/dist/{chunk-QT4D5XCI.js → chunk-TAWRJ24T.js} +1 -1
- package/dist/{chunk-AI47FDGL.js → chunk-UO4MZQNO.js} +1 -1
- package/dist/chunk-UTKUO2KY.js +102 -0
- package/dist/{chunk-CDKQJWGK.js → chunk-VDZ4I4ZP.js} +160 -18
- package/dist/cli.js +16 -16
- package/dist/{clock-ZT5YT6HA.js → clock-SH4NQC2F.js} +3 -3
- package/dist/{cloud-sync-GPXYPTIC.js → cloud-sync-MJVJ33LD.js} +11 -10
- package/dist/{daemon-restart-2EWBRTN5.js → daemon-restart-BIGU4OKS.js} +1 -1
- package/dist/daemon.js +543 -236
- package/dist/{delete-O7PEFQDF.js → delete-CYVZUAV2.js} +4 -0
- package/dist/{delivery-manager-HGVZSEES.js → delivery-manager-FDHCZ7PU.js} +7 -5
- package/dist/delivery-notices-65TB3CBX.js +18 -0
- package/dist/{echo-text-6ANI5HHN.js → echo-text-KU6G4Z7V.js} +12 -11
- package/dist/{extensions-NY6WST33.js → extensions-VACUKFA5.js} +11 -10
- package/dist/{files-BRY5RRIM.js → files-CHPNEQXN.js} +5 -4
- package/dist/{join-PYA36HEC.js → join-KLRU72JJ.js} +10 -2
- package/dist/{message-delivery-JHMAJ2GU.js → message-delivery-TUXM3AAE.js} +15 -12
- package/dist/{mind-WF6QIXWV.js → mind-IX5AWUST.js} +13 -13
- package/dist/{mind-contacts-QYU4TPEA.js → mind-contacts-BKUOUJPV.js} +5 -5
- package/dist/{mind-history-OGNR4L7W.js → mind-history-RVF6KJ7K.js} +83 -11
- package/dist/{mind-list-SYRA7CH5.js → mind-list-KNU34SUD.js} +2 -2
- package/dist/{mind-service-DZCUUC7J.js → mind-service-WHFBUKZE.js} +11 -10
- package/dist/{mind-sleep-LZAFBDZD.js → mind-sleep-D3DEYAWG.js} +5 -5
- package/dist/{mind-status-Z2LQFA6F.js → mind-status-BPTLQ5Z6.js} +15 -3
- package/dist/{package-B6O5APCZ.js → package-BN5KMU54.js} +2 -2
- package/dist/{prompts-NBO3HRK3.js → prompts-HNKGCAEY.js} +1 -1
- package/dist/{reject-TWB4QJHG.js → reject-K2A4UK2Y.js} +9 -5
- package/dist/{scheduler-TNBN54ZK.js → scheduler-5X5UIQOQ.js} +7 -5
- package/dist/{seed-K2LYODIH.js → seed-LHZ33UB4.js} +1 -1
- package/dist/{seed-cmd-2EWBTR6P.js → seed-cmd-U4RVJ3UA.js} +2 -2
- package/dist/{seed-create-VGTLU5FD.js → seed-create-OQU4KNCB.js} +1 -1
- package/dist/{seed-readiness-DYOVCYHN.js → seed-readiness-WJYYRRCK.js} +2 -2
- package/dist/{seed-sprout-PILHJ3YS.js → seed-sprout-SFUYH2RG.js} +7 -7
- package/dist/{send-2YKOZJJ5.js → send-FWZAMDBN.js} +35 -20
- package/dist/{setup-MDPSK2XG.js → setup-RVSGCIF3.js} +1 -1
- package/dist/skills/memory/SKILL.md +47 -43
- package/dist/skills/volute-mind/references/variants.md +1 -1
- package/dist/{skills-NH2GQKAF.js → skills-QRPU6BWY.js} +1 -1
- package/dist/{sleep-manager-7JXEQQ2P.js → sleep-manager-NWUCRBZB.js} +11 -10
- package/dist/{spirit-25JVAL3M.js → spirit-I5ECLGRI.js} +16 -2
- package/dist/{spirit-availability-PJDWZJ3J.js → spirit-availability-NIUUYKU2.js} +12 -11
- package/dist/{sprout-EXUZMIOR.js → sprout-A2PMEF7Z.js} +1 -1
- package/dist/{system-events-ZPSH63QX.js → system-events-YTOU66QI.js} +7 -1
- package/dist/{systems-VDRUZXNF.js → systems-2DIJY4RF.js} +6 -6
- package/dist/{turn-tracker-TAT2HGK6.js → turn-tracker-NNHS6MAR.js} +4 -2
- package/dist/{up-J7XCAH7P.js → up-UGBHH743.js} +1 -1
- package/dist/{variant-cleanup-YXN25ZVW.js → variant-cleanup-CZDLIY3F.js} +7 -5
- package/dist/{version-notify-EN3KJ7HH.js → version-notify-SJVAQLZI.js} +4 -4
- package/dist/web-assets/assets/index-BHSU9r-4.css +1 -0
- package/dist/web-assets/assets/index-b7G7ZsVk.js +97 -0
- package/dist/web-assets/index.html +2 -2
- package/package.json +2 -2
- package/templates/_base/.init/.config/prompts.json +1 -1
- package/templates/_base/src/lib/auto-commit.ts +49 -11
- package/templates/_base/src/lib/codex-session-seed.ts +463 -0
- package/templates/_base/src/lib/daemon-client.ts +35 -0
- package/templates/_base/src/lib/pi-session-seed.ts +434 -0
- package/templates/_base/src/lib/seed-note.ts +78 -0
- package/templates/_base/src/lib/session-seed.ts +375 -0
- package/templates/_base/src/lib/startup.ts +108 -4
- package/templates/claude/.init/CLAUDE.md +3 -3
- package/templates/claude/src/agent.ts +267 -89
- package/templates/claude/src/lib/hooks/pre-compact.ts +30 -5
- package/templates/claude/src/lib/stream-consumer.ts +13 -0
- package/templates/claude/src/server.ts +2 -1
- package/templates/codex/.init/AGENTS.md +3 -3
- package/templates/codex/src/agent.ts +256 -39
- package/templates/codex/src/server.ts +2 -1
- package/templates/pi/.init/MINDS.md +3 -3
- package/templates/pi/src/agent.ts +269 -43
- package/templates/pi/src/lib/event-handler.ts +11 -0
- package/templates/pi/src/server.ts +3 -1
- package/dist/web-assets/assets/index-BOyJ4Gqs.css +0 -1
- package/dist/web-assets/assets/index-CrxBwnzp.js +0 -95
- package/dist/{backup-YVGV2ZNM.js → backup-WWXVIMYQ.js} +5 -5
- package/dist/{bridge-YK7NTRU5.js → bridge-KU2IKSH3.js} +3 -3
- package/dist/{channels-FSAHOQWI.js → channels-LW5ZLMSQ.js} +3 -3
- package/dist/{chunk-IWAVIJO2.js → chunk-BDHKCLWK.js} +0 -0
- package/dist/{chunk-EEOMA4DX.js → chunk-HTB7KDRU.js} +3 -3
- package/dist/{create-M727VUDF.js → create-C3XZACTV.js} +3 -3
- package/dist/{env-IBW2QI6K.js → env-BJGM6FBA.js} +5 -5
- package/dist/{extension-MRYJWSMV.js → extension-B2W52WZY.js} +3 -3
- package/dist/{list-UQGIW7OF.js → list-R2GDWOEE.js} +3 -3
- package/dist/{login-RJJTCVR3.js → login-EZ4RRDQX.js} +3 -3
- package/dist/{login-SB5OENMV.js → login-JDOAK465.js} +4 -4
- package/dist/{logout-YFZAJRPY.js → logout-K6ZGAJY5.js} +3 -3
- package/dist/{logout-BYV433X6.js → logout-MWSIUOQU.js} +3 -3
- package/dist/{mind-wake-LDGPRA42.js → mind-wake-DCDFFQKX.js} +3 -3
- package/dist/{read-RJ5NAW5J.js → read-J5RM674N.js} +3 -3
- package/dist/{register-OX4HRSGC.js → register-53LBGSDC.js} +3 -3
- package/dist/{restart-AGEGNSYQ.js → restart-R2WFNF5K.js} +4 -4
- package/dist/{skill-B5OJEQUA.js → skill-DXLLSUHB.js} +5 -5
- package/dist/{start-MUMNFR52.js → start-XDOUAWN3.js} +4 -4
- package/dist/{status-MWLE3RVC.js → status-EYG4A3OK.js} +3 -3
- package/dist/{stop-EKERNYTX.js → stop-O62RTEMD.js} +4 -4
package/dist/daemon.js
CHANGED
|
@@ -14,23 +14,31 @@ import {
|
|
|
14
14
|
rejectPending,
|
|
15
15
|
stageFile,
|
|
16
16
|
validateFilePath
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-F6D766VQ.js";
|
|
18
18
|
import {
|
|
19
19
|
findOpenClawSession,
|
|
20
20
|
importOpenClawConnectors,
|
|
21
21
|
importPiSession,
|
|
22
22
|
parseNameFromIdentity
|
|
23
23
|
} from "./chunk-I7U5EEZC.js";
|
|
24
|
+
import {
|
|
25
|
+
getMemoryStatus
|
|
26
|
+
} from "./chunk-5RBEEAKW.js";
|
|
27
|
+
import {
|
|
28
|
+
checkForUpdate,
|
|
29
|
+
checkForUpdateCached,
|
|
30
|
+
getCurrentVersion
|
|
31
|
+
} from "./chunk-LAUCO2HC.js";
|
|
24
32
|
import {
|
|
25
33
|
SPIRIT_NOTICE_PREFIX,
|
|
26
34
|
ensureSpiritAvailable
|
|
27
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-P2TBUKLW.js";
|
|
28
36
|
import {
|
|
29
37
|
isHomeOnlyArchive
|
|
30
38
|
} from "./chunk-FLIEXG2T.js";
|
|
31
39
|
import {
|
|
32
40
|
evaluateSeedChecklist
|
|
33
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-RMADJFA7.js";
|
|
34
42
|
import {
|
|
35
43
|
isTemplateStale
|
|
36
44
|
} from "./chunk-CV6BNGFK.js";
|
|
@@ -39,19 +47,14 @@ import {
|
|
|
39
47
|
} from "./chunk-HTRSSOHY.js";
|
|
40
48
|
import {
|
|
41
49
|
cleanupVariant
|
|
42
|
-
} from "./chunk-
|
|
43
|
-
import {
|
|
44
|
-
checkForUpdate,
|
|
45
|
-
checkForUpdateCached,
|
|
46
|
-
getCurrentVersion
|
|
47
|
-
} from "./chunk-LAUCO2HC.js";
|
|
50
|
+
} from "./chunk-UO4MZQNO.js";
|
|
48
51
|
import {
|
|
49
52
|
PLATFORMS,
|
|
50
53
|
echoTextToChannel,
|
|
51
54
|
fixModelEscapes,
|
|
52
55
|
getPlatformDriver,
|
|
53
56
|
routeOutboundBridge
|
|
54
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-7ARONCZW.js";
|
|
55
58
|
import {
|
|
56
59
|
getBridgeConfig,
|
|
57
60
|
readBridgesConfig,
|
|
@@ -104,9 +107,10 @@ import {
|
|
|
104
107
|
startMindFull,
|
|
105
108
|
stopMindFull,
|
|
106
109
|
uninstallNpmExtension,
|
|
110
|
+
willGateMessage,
|
|
107
111
|
writeSystemsConfig
|
|
108
|
-
} from "./chunk-
|
|
109
|
-
import "./chunk-
|
|
112
|
+
} from "./chunk-HASADR3E.js";
|
|
113
|
+
import "./chunk-5DBGQBQR.js";
|
|
110
114
|
import {
|
|
111
115
|
applyInitFiles,
|
|
112
116
|
applyTemplateHomeFiles,
|
|
@@ -119,7 +123,10 @@ import {
|
|
|
119
123
|
import {
|
|
120
124
|
getScheduler,
|
|
121
125
|
initScheduler
|
|
122
|
-
} from "./chunk-
|
|
126
|
+
} from "./chunk-PIZDPY4G.js";
|
|
127
|
+
import {
|
|
128
|
+
recordDeliveryFailure
|
|
129
|
+
} from "./chunk-UTKUO2KY.js";
|
|
123
130
|
import {
|
|
124
131
|
checkStaleSend,
|
|
125
132
|
formatHoldNotice,
|
|
@@ -128,7 +135,7 @@ import {
|
|
|
128
135
|
initDeliveryManager,
|
|
129
136
|
isConversationId,
|
|
130
137
|
publishTypingForChannels
|
|
131
|
-
} from "./chunk-
|
|
138
|
+
} from "./chunk-RQRO6PC7.js";
|
|
132
139
|
import {
|
|
133
140
|
MindStartupError,
|
|
134
141
|
RestartTracker,
|
|
@@ -138,7 +145,7 @@ import {
|
|
|
138
145
|
initMindManager,
|
|
139
146
|
syncProviderToMinds,
|
|
140
147
|
tryGetMindManager
|
|
141
|
-
} from "./chunk-
|
|
148
|
+
} from "./chunk-23UJGVYP.js";
|
|
142
149
|
import {
|
|
143
150
|
extractTextContent
|
|
144
151
|
} from "./chunk-UDZLPR34.js";
|
|
@@ -155,13 +162,15 @@ import {
|
|
|
155
162
|
initSummarizer,
|
|
156
163
|
isoWeekKeyForDateStr,
|
|
157
164
|
markErrored,
|
|
165
|
+
parseDbTimestamp,
|
|
158
166
|
parseUtcDateTime,
|
|
159
167
|
summarizeOrphanedTurns,
|
|
160
168
|
summarizeTurn,
|
|
169
|
+
supersedeTurnSummary,
|
|
161
170
|
takeErrored,
|
|
162
171
|
trackToolUse,
|
|
163
172
|
utcDateTimeStr
|
|
164
|
-
} from "./chunk-
|
|
173
|
+
} from "./chunk-VDZ4I4ZP.js";
|
|
165
174
|
import {
|
|
166
175
|
SEED_SKILLS,
|
|
167
176
|
STANDARD_SKILLS,
|
|
@@ -183,12 +192,13 @@ import {
|
|
|
183
192
|
syncBuiltinSkills,
|
|
184
193
|
uninstallSkill,
|
|
185
194
|
updateSkill
|
|
186
|
-
} from "./chunk-
|
|
195
|
+
} from "./chunk-TAWRJ24T.js";
|
|
187
196
|
import {
|
|
188
197
|
approveUser,
|
|
189
198
|
changePassword,
|
|
190
199
|
countAdmins,
|
|
191
200
|
createUser,
|
|
201
|
+
deleteExternalMindUser,
|
|
192
202
|
deleteMindUser as deleteMindUser2,
|
|
193
203
|
deleteUser,
|
|
194
204
|
getDisplayNames,
|
|
@@ -204,9 +214,11 @@ import {
|
|
|
204
214
|
updateUserProfile,
|
|
205
215
|
verifyUser,
|
|
206
216
|
withSenderDisplayNames
|
|
207
|
-
} from "./chunk-
|
|
217
|
+
} from "./chunk-7LT3YZUR.js";
|
|
208
218
|
import {
|
|
219
|
+
MIND_LEVEL_THREAD,
|
|
209
220
|
captureReflection,
|
|
221
|
+
cleanExpiredEvents,
|
|
210
222
|
clearDeliveredEvents,
|
|
211
223
|
deliverEvent,
|
|
212
224
|
drainEvents,
|
|
@@ -217,7 +229,7 @@ import {
|
|
|
217
229
|
listEvents,
|
|
218
230
|
parseMeta,
|
|
219
231
|
recordNotice
|
|
220
|
-
} from "./chunk-
|
|
232
|
+
} from "./chunk-IQ7VNWH6.js";
|
|
221
233
|
import {
|
|
222
234
|
publish as publish2,
|
|
223
235
|
subscribe as subscribe2,
|
|
@@ -230,7 +242,7 @@ import {
|
|
|
230
242
|
getPrompt,
|
|
231
243
|
getPromptIfCustom,
|
|
232
244
|
substitute
|
|
233
|
-
} from "./chunk-
|
|
245
|
+
} from "./chunk-AP2KQMMW.js";
|
|
234
246
|
import {
|
|
235
247
|
isSandboxEnabled,
|
|
236
248
|
wrapForSandbox
|
|
@@ -395,9 +407,9 @@ import "./chunk-K3NQKI34.js";
|
|
|
395
407
|
|
|
396
408
|
// packages/daemon/src/daemon.ts
|
|
397
409
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
398
|
-
import { chmodSync, mkdirSync as mkdirSync10, readFileSync as readFileSync12, unlinkSync as unlinkSync2, writeFileSync as
|
|
410
|
+
import { chmodSync, mkdirSync as mkdirSync10, readFileSync as readFileSync12, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
|
|
399
411
|
import { homedir as homedir3 } from "os";
|
|
400
|
-
import { resolve as
|
|
412
|
+
import { resolve as resolve20 } from "path";
|
|
401
413
|
import { format } from "util";
|
|
402
414
|
|
|
403
415
|
// packages/daemon/src/lib/daemon/backup-manager.ts
|
|
@@ -508,7 +520,7 @@ var BackupManager = class {
|
|
|
508
520
|
} catch (pubErr) {
|
|
509
521
|
blog.error("failed to publish backup-failure activity", logger_default.errorData(pubErr));
|
|
510
522
|
}
|
|
511
|
-
const { deliverEvent: deliverEvent2 } = await import("./system-events-
|
|
523
|
+
const { deliverEvent: deliverEvent2 } = await import("./system-events-YTOU66QI.js");
|
|
512
524
|
await deliverEvent2(getSpiritName(), {
|
|
513
525
|
type: "notice",
|
|
514
526
|
meta: { subtype: "backup" },
|
|
@@ -732,8 +744,8 @@ var BridgeManager = class {
|
|
|
732
744
|
if (!tracked) return;
|
|
733
745
|
this.stopping.add(platform);
|
|
734
746
|
this.bridges.delete(platform);
|
|
735
|
-
await new Promise((
|
|
736
|
-
tracked.child.on("exit", () =>
|
|
747
|
+
await new Promise((resolve21) => {
|
|
748
|
+
tracked.child.on("exit", () => resolve21());
|
|
737
749
|
try {
|
|
738
750
|
if (tracked.child.pid) {
|
|
739
751
|
process.kill(-tracked.child.pid, "SIGTERM");
|
|
@@ -744,7 +756,7 @@ var BridgeManager = class {
|
|
|
744
756
|
if (err instanceof Error && err.code !== "ESRCH") {
|
|
745
757
|
blog2.warn(`failed to stop bridge ${platform}`, logger_default.errorData(err));
|
|
746
758
|
}
|
|
747
|
-
|
|
759
|
+
resolve21();
|
|
748
760
|
}
|
|
749
761
|
setTimeout(() => {
|
|
750
762
|
try {
|
|
@@ -755,7 +767,7 @@ var BridgeManager = class {
|
|
|
755
767
|
}
|
|
756
768
|
} catch {
|
|
757
769
|
}
|
|
758
|
-
|
|
770
|
+
resolve21();
|
|
759
771
|
}, 5e3);
|
|
760
772
|
});
|
|
761
773
|
this.stopping.delete(platform);
|
|
@@ -865,6 +877,11 @@ async function runMaintenance() {
|
|
|
865
877
|
} catch (err) {
|
|
866
878
|
logger_default.warn("maintenance: failed to clean expired logs", logger_default.errorData(err));
|
|
867
879
|
}
|
|
880
|
+
try {
|
|
881
|
+
await cleanExpiredEvents();
|
|
882
|
+
} catch (err) {
|
|
883
|
+
logger_default.warn("maintenance: failed to clean expired events", logger_default.errorData(err));
|
|
884
|
+
}
|
|
868
885
|
}
|
|
869
886
|
function startMaintenanceInterval(intervalMs = MAINTENANCE_INTERVAL_MS, task = runMaintenance) {
|
|
870
887
|
const handle = setInterval(() => void task(), intervalMs);
|
|
@@ -1427,10 +1444,10 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1427
1444
|
stream.writeSSE({ data: JSON.stringify(entry) }).catch(() => {
|
|
1428
1445
|
});
|
|
1429
1446
|
});
|
|
1430
|
-
await new Promise((
|
|
1447
|
+
await new Promise((resolve21) => {
|
|
1431
1448
|
stream.onAbort(() => {
|
|
1432
1449
|
unsubscribe();
|
|
1433
|
-
|
|
1450
|
+
resolve21();
|
|
1434
1451
|
});
|
|
1435
1452
|
});
|
|
1436
1453
|
});
|
|
@@ -1455,7 +1472,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1455
1472
|
config2.name = name.trim() || void 0;
|
|
1456
1473
|
writeGlobalConfig(config2);
|
|
1457
1474
|
if (config2.name !== previousName) {
|
|
1458
|
-
const { notifySpiritSystemChange } = await import("./spirit-
|
|
1475
|
+
const { notifySpiritSystemChange } = await import("./spirit-I5ECLGRI.js");
|
|
1459
1476
|
await notifySpiritSystemChange();
|
|
1460
1477
|
}
|
|
1461
1478
|
return c.json({ name: config2.name ?? null });
|
|
@@ -1814,8 +1831,8 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1814
1831
|
createdAt: Date.now()
|
|
1815
1832
|
};
|
|
1816
1833
|
oauthFlows.set(flowId, flow);
|
|
1817
|
-
const promptPromise = needsManualCode ? new Promise((
|
|
1818
|
-
flow.resolveCode =
|
|
1834
|
+
const promptPromise = needsManualCode ? new Promise((resolve21) => {
|
|
1835
|
+
flow.resolveCode = resolve21;
|
|
1819
1836
|
}) : void 0;
|
|
1820
1837
|
oauthProvider.login({
|
|
1821
1838
|
onAuth: (info) => {
|
|
@@ -1854,7 +1871,7 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
|
|
|
1854
1871
|
existing.error = err instanceof Error ? err.message : String(err);
|
|
1855
1872
|
}
|
|
1856
1873
|
});
|
|
1857
|
-
await new Promise((
|
|
1874
|
+
await new Promise((resolve21) => setTimeout(resolve21, 2e3));
|
|
1858
1875
|
const state = oauthFlows.get(flowId);
|
|
1859
1876
|
return c.json({
|
|
1860
1877
|
flowId,
|
|
@@ -2115,8 +2132,8 @@ var app2 = new Hono2().get("/events", async (c) => {
|
|
|
2115
2132
|
});
|
|
2116
2133
|
}, 15e3);
|
|
2117
2134
|
cleanups.push(() => clearInterval(keepAlive));
|
|
2118
|
-
await new Promise((
|
|
2119
|
-
stream.onAbort(() =>
|
|
2135
|
+
await new Promise((resolve21) => {
|
|
2136
|
+
stream.onAbort(() => resolve21());
|
|
2120
2137
|
});
|
|
2121
2138
|
} finally {
|
|
2122
2139
|
for (const cleanup of cleanups) {
|
|
@@ -2149,6 +2166,11 @@ function isNotModified(c, etag) {
|
|
|
2149
2166
|
}
|
|
2150
2167
|
|
|
2151
2168
|
// packages/daemon/src/web/api/auth.ts
|
|
2169
|
+
async function withExternalFlag(list) {
|
|
2170
|
+
if (!list.some((u) => u.user_type === "mind")) return list;
|
|
2171
|
+
const local = new Set((await readAllMinds()).map((m) => m.name));
|
|
2172
|
+
return list.map((u) => u.user_type === "mind" ? { ...u, external: !local.has(u.username) } : u);
|
|
2173
|
+
}
|
|
2152
2174
|
function tryJoinSystem(userId) {
|
|
2153
2175
|
if (!process.env.VOLUTE_DAEMON_TOKEN) return;
|
|
2154
2176
|
joinSystemChannel(userId).catch(() => {
|
|
@@ -2302,9 +2324,9 @@ var admin = new Hono3().use(authMiddleware).get("/users", async (c) => {
|
|
|
2302
2324
|
}
|
|
2303
2325
|
const type = c.req.query("type");
|
|
2304
2326
|
if (type === "human" || type === "mind") {
|
|
2305
|
-
return c.json(await listUsersByType(type));
|
|
2327
|
+
return c.json(await withExternalFlag(await listUsersByType(type)));
|
|
2306
2328
|
}
|
|
2307
|
-
return c.json(await listUsers());
|
|
2329
|
+
return c.json(await withExternalFlag(await listUsers()));
|
|
2308
2330
|
}).get("/users/pending", async (c) => {
|
|
2309
2331
|
const user = c.get("user");
|
|
2310
2332
|
if (user.role !== "admin") return c.json({ error: "Forbidden" }, 403);
|
|
@@ -2401,7 +2423,11 @@ var admin = new Hono3().use(authMiddleware).get("/users", async (c) => {
|
|
|
2401
2423
|
if (adminCount <= 1) return c.json({ error: "Cannot delete the last admin" }, 400);
|
|
2402
2424
|
}
|
|
2403
2425
|
if (target.user_type === "mind") {
|
|
2404
|
-
|
|
2426
|
+
if (await findMind(target.username)) {
|
|
2427
|
+
return c.json({ error: "Use the mind deletion API to delete minds" }, 400);
|
|
2428
|
+
}
|
|
2429
|
+
await deleteExternalMindUser(id);
|
|
2430
|
+
return c.json({ ok: true });
|
|
2405
2431
|
}
|
|
2406
2432
|
await deleteUser(id);
|
|
2407
2433
|
return c.json({ ok: true });
|
|
@@ -2658,6 +2684,21 @@ async function fanOutToMinds(opts) {
|
|
|
2658
2684
|
const manager = getMindManager();
|
|
2659
2685
|
const sm = getSleepManagerIfReady();
|
|
2660
2686
|
const registeredMinds = new Set((await readAllMinds()).map((m) => m.name));
|
|
2687
|
+
const senderIsMind = registeredMinds.has(opts.senderName);
|
|
2688
|
+
let senderCtx;
|
|
2689
|
+
const reportFailure = (reason) => {
|
|
2690
|
+
if (!senderIsMind) return;
|
|
2691
|
+
senderCtx ??= getBaseName(opts.senderName).then((base) => ({
|
|
2692
|
+
base,
|
|
2693
|
+
channel: buildVoluteSlug({
|
|
2694
|
+
participants,
|
|
2695
|
+
mindUsername: base,
|
|
2696
|
+
conversationId: opts.conversationId,
|
|
2697
|
+
...opts.slugExtra
|
|
2698
|
+
})
|
|
2699
|
+
}));
|
|
2700
|
+
senderCtx.then(({ base, channel }) => recordDeliveryFailure({ mind: base, channel, reason })).catch((err) => logger_default.warn("fan-out: failed to report send failure", logger_default.errorData(err)));
|
|
2701
|
+
};
|
|
2661
2702
|
const targetMinds = mindParticipants.map((ap) => {
|
|
2662
2703
|
const key = opts.targetName ? opts.targetName(ap.username) : ap.username;
|
|
2663
2704
|
if (manager.isRunning(key) || sm?.isSleeping(ap.username)) return ap.username;
|
|
@@ -2666,21 +2707,46 @@ async function fanOutToMinds(opts) {
|
|
|
2666
2707
|
logger_default.warn(
|
|
2667
2708
|
`fan-out: skipping ${ap.username} (not running) for conversation ${opts.conversationId}`
|
|
2668
2709
|
);
|
|
2710
|
+
reportFailure(`${ap.username} is not running`);
|
|
2711
|
+
} else if (ap.userType === "mind") {
|
|
2712
|
+
logger_default.info(
|
|
2713
|
+
`fan-out: skipping ${ap.username} (no registry row \u2014 external mind or stale participant) for conversation ${opts.conversationId}`
|
|
2714
|
+
);
|
|
2669
2715
|
}
|
|
2670
2716
|
if (ap.userType === "system") {
|
|
2671
|
-
import("./spirit-availability-
|
|
2717
|
+
import("./spirit-availability-NIUUYKU2.js").then(({ ensureSpiritAvailable: ensureSpiritAvailable2 }) => ensureSpiritAvailable2()).catch((err) => logger_default.warn("fan-out: on-demand spirit start failed", logger_default.errorData(err)));
|
|
2672
2718
|
}
|
|
2673
2719
|
}
|
|
2674
2720
|
return null;
|
|
2675
2721
|
}).filter((n) => n !== null && n !== opts.senderName);
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2722
|
+
const targets = targetMinds.map((mindName) => ({
|
|
2723
|
+
mindName,
|
|
2724
|
+
target: opts.targetName ? opts.targetName(mindName) : mindName,
|
|
2725
|
+
channel: buildVoluteSlug({
|
|
2679
2726
|
participants,
|
|
2680
2727
|
mindUsername: mindName,
|
|
2681
2728
|
conversationId: opts.conversationId,
|
|
2682
2729
|
...opts.slugExtra
|
|
2683
|
-
})
|
|
2730
|
+
})
|
|
2731
|
+
}));
|
|
2732
|
+
const gatedRecipients = (await Promise.all(
|
|
2733
|
+
targets.map(async ({ mindName, target, channel }) => {
|
|
2734
|
+
if (sm?.isSleeping(mindName)) return null;
|
|
2735
|
+
try {
|
|
2736
|
+
const gated = await willGateMessage(target, {
|
|
2737
|
+
channel,
|
|
2738
|
+
sender: opts.senderName,
|
|
2739
|
+
isDM,
|
|
2740
|
+
participantCount: participants.length
|
|
2741
|
+
});
|
|
2742
|
+
return gated ? mindName : null;
|
|
2743
|
+
} catch (err) {
|
|
2744
|
+
logger_default.warn(`fan-out: will-gate check failed for ${target}`, logger_default.errorData(err));
|
|
2745
|
+
return null;
|
|
2746
|
+
}
|
|
2747
|
+
})
|
|
2748
|
+
)).filter((n) => n !== null);
|
|
2749
|
+
for (const { mindName, target, channel } of targets) {
|
|
2684
2750
|
const typingMap = getTypingMap();
|
|
2685
2751
|
const currentlyTyping = typingMap.get(channel).filter((name) => participantNames.includes(name));
|
|
2686
2752
|
deliverMessage(target, {
|
|
@@ -2692,10 +2758,20 @@ async function fanOutToMinds(opts) {
|
|
|
2692
2758
|
participantCount: participants.length,
|
|
2693
2759
|
isDM,
|
|
2694
2760
|
...currentlyTyping.length > 0 ? { typing: currentlyTyping } : {}
|
|
2695
|
-
}).
|
|
2696
|
-
|
|
2697
|
-
|
|
2761
|
+
}).then(
|
|
2762
|
+
(ok) => {
|
|
2763
|
+
if (!ok) {
|
|
2764
|
+
logger_default.warn(`fan-out delivery failed for ${target} (message not delivered)`);
|
|
2765
|
+
reportFailure(`the delivery to ${mindName} failed`);
|
|
2766
|
+
}
|
|
2767
|
+
},
|
|
2768
|
+
(err) => {
|
|
2769
|
+
logger_default.warn(`fan-out delivery failed for ${target}`, logger_default.errorData(err));
|
|
2770
|
+
reportFailure(`the delivery to ${mindName} failed`);
|
|
2771
|
+
}
|
|
2772
|
+
);
|
|
2698
2773
|
}
|
|
2774
|
+
return { gatedRecipients };
|
|
2699
2775
|
}
|
|
2700
2776
|
|
|
2701
2777
|
// packages/daemon/src/web/api/bridges.ts
|
|
@@ -3053,12 +3129,18 @@ import { resolve as resolve4 } from "path";
|
|
|
3053
3129
|
import { Hono as Hono10 } from "hono";
|
|
3054
3130
|
async function notifyMind(mindName, message) {
|
|
3055
3131
|
const entry = await findMind(mindName);
|
|
3056
|
-
if (!entry) return;
|
|
3057
|
-
try {
|
|
3058
|
-
const { deliverEvent: deliverEvent2 } = await import("./system-events-
|
|
3059
|
-
await deliverEvent2(mindName, { type: "file-share", body: message });
|
|
3132
|
+
if (!entry) return false;
|
|
3133
|
+
try {
|
|
3134
|
+
const { deliverEvent: deliverEvent2 } = await import("./system-events-YTOU66QI.js");
|
|
3135
|
+
const result = await deliverEvent2(mindName, { type: "file-share", body: message });
|
|
3136
|
+
if (result.delivered) return true;
|
|
3137
|
+
if (result.id == null) return false;
|
|
3138
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
|
|
3139
|
+
const { getBaseName: getBaseName2 } = await import("./registry-TRC2HJXR.js");
|
|
3140
|
+
return getSleepManagerIfReady2()?.isSleeping(await getBaseName2(mindName)) ?? false;
|
|
3060
3141
|
} catch (err) {
|
|
3061
3142
|
logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
|
|
3143
|
+
return false;
|
|
3062
3144
|
}
|
|
3063
3145
|
}
|
|
3064
3146
|
var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
|
|
@@ -3099,11 +3181,11 @@ var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
|
|
|
3099
3181
|
const filename = body.filePath;
|
|
3100
3182
|
const sizeStr = formatFileSize(content.length);
|
|
3101
3183
|
const { id } = stageFile(body.targetMind, senderName, filename, content, body.filePath);
|
|
3102
|
-
await notifyMind(
|
|
3184
|
+
const notified = await notifyMind(
|
|
3103
3185
|
body.targetMind,
|
|
3104
3186
|
`[file] ${senderName} sent ${filename} (${sizeStr}) \u2014 run: volute chat accept ${id}`
|
|
3105
3187
|
);
|
|
3106
|
-
return c.json({ status: "pending", id }, 200);
|
|
3188
|
+
return c.json({ status: "pending", id, notified }, 200);
|
|
3107
3189
|
}).get("/:name/files/pending", requireSelf(), async (c) => {
|
|
3108
3190
|
const name = c.req.param("name");
|
|
3109
3191
|
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -3128,8 +3210,8 @@ var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
|
|
|
3128
3210
|
}
|
|
3129
3211
|
return c.json({ error: `Failed to accept file: ${message}` }, 500);
|
|
3130
3212
|
}
|
|
3131
|
-
await notifyMind(result.sender, `[file] ${name} accepted ${result.filename}`);
|
|
3132
|
-
return c.json({ ok: true, destPath: result.destPath });
|
|
3213
|
+
const notified = await notifyMind(result.sender, `[file] ${name} accepted ${result.filename}`);
|
|
3214
|
+
return c.json({ ok: true, destPath: result.destPath, notified });
|
|
3133
3215
|
}).post("/:name/files/reject", requireSelf(), async (c) => {
|
|
3134
3216
|
const name = c.req.param("name");
|
|
3135
3217
|
if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
|
|
@@ -3145,8 +3227,8 @@ var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
|
|
|
3145
3227
|
}
|
|
3146
3228
|
return c.json({ error: `Failed to reject file: ${message}` }, 500);
|
|
3147
3229
|
}
|
|
3148
|
-
await notifyMind(result.sender, `[file] ${name} rejected ${result.filename}`);
|
|
3149
|
-
return c.json({ ok: true });
|
|
3230
|
+
const notified = await notifyMind(result.sender, `[file] ${name} rejected ${result.filename}`);
|
|
3231
|
+
return c.json({ ok: true, notified });
|
|
3150
3232
|
}).post("/:name/files/stage", requireSelf(), async (c) => {
|
|
3151
3233
|
const receiverName = c.req.param("name");
|
|
3152
3234
|
const receiverEntry = await findMind(receiverName);
|
|
@@ -3169,11 +3251,11 @@ var app8 = new Hono10().post("/:name/files/send", requireSelf(), async (c) => {
|
|
|
3169
3251
|
}
|
|
3170
3252
|
const sizeStr = formatFileSize(content.length);
|
|
3171
3253
|
const { id } = stageFile(receiverName, body.sender, body.filename, content, body.filename);
|
|
3172
|
-
await notifyMind(
|
|
3254
|
+
const notified = await notifyMind(
|
|
3173
3255
|
receiverName,
|
|
3174
3256
|
`[file] ${body.sender} sent ${body.filename} (${sizeStr}) \u2014 run: volute chat accept ${id}`
|
|
3175
3257
|
);
|
|
3176
|
-
return c.json({ status: "pending", id }, 200);
|
|
3258
|
+
return c.json({ status: "pending", id, notified }, 200);
|
|
3177
3259
|
});
|
|
3178
3260
|
var file_sharing_default = app8;
|
|
3179
3261
|
|
|
@@ -4026,9 +4108,9 @@ var app11 = new Hono14().get("/:name/logs", requireSelf(), async (c) => {
|
|
|
4026
4108
|
stream.onAbort(() => {
|
|
4027
4109
|
tail.kill();
|
|
4028
4110
|
});
|
|
4029
|
-
await new Promise((
|
|
4030
|
-
tail.on("exit",
|
|
4031
|
-
stream.onAbort(
|
|
4111
|
+
await new Promise((resolve21) => {
|
|
4112
|
+
tail.on("exit", resolve21);
|
|
4113
|
+
stream.onAbort(resolve21);
|
|
4032
4114
|
});
|
|
4033
4115
|
});
|
|
4034
4116
|
}).get("/:name/logs/tail", requireSelf(), async (c) => {
|
|
@@ -4046,8 +4128,8 @@ var app11 = new Hono14().get("/:name/logs", requireSelf(), async (c) => {
|
|
|
4046
4128
|
tail.stdout.on("data", (data) => {
|
|
4047
4129
|
output += data.toString();
|
|
4048
4130
|
});
|
|
4049
|
-
await new Promise((
|
|
4050
|
-
tail.on("exit",
|
|
4131
|
+
await new Promise((resolve21) => {
|
|
4132
|
+
tail.on("exit", resolve21);
|
|
4051
4133
|
});
|
|
4052
4134
|
return c.text(output);
|
|
4053
4135
|
});
|
|
@@ -4147,7 +4229,7 @@ import {
|
|
|
4147
4229
|
rmSync as rmSync3,
|
|
4148
4230
|
writeFileSync as writeFileSync8
|
|
4149
4231
|
} from "fs";
|
|
4150
|
-
import { relative, resolve as
|
|
4232
|
+
import { relative, resolve as resolve13 } from "path";
|
|
4151
4233
|
import { zValidator as zValidator6 } from "@hono/zod-validator";
|
|
4152
4234
|
import { and as and5, desc as desc3, eq as eq5, sql as sql3 } from "drizzle-orm";
|
|
4153
4235
|
import { Hono as Hono16 } from "hono";
|
|
@@ -4675,6 +4757,8 @@ function deriveThinkingLevel(config2) {
|
|
|
4675
4757
|
}
|
|
4676
4758
|
|
|
4677
4759
|
// packages/daemon/src/lib/mind/variants.ts
|
|
4760
|
+
import { statSync as statSync3 } from "fs";
|
|
4761
|
+
import { resolve as resolve11 } from "path";
|
|
4678
4762
|
var SAFE_BRANCH_RE = /^[a-zA-Z0-9._\-/]+$/;
|
|
4679
4763
|
function validateBranchName(branch) {
|
|
4680
4764
|
if (!SAFE_BRANCH_RE.test(branch)) {
|
|
@@ -4687,6 +4771,55 @@ function validateBranchName(branch) {
|
|
|
4687
4771
|
}
|
|
4688
4772
|
var MERGE_EXCLUDED_PATHS = ["home/MEMORY.md", "home/memory/journal/"];
|
|
4689
4773
|
var MAX_DELTA_CHARS = 12e3;
|
|
4774
|
+
var NEVER_REPORT_PREFIXES = ["home/.claude/", "home/.pi/", "home/.agents/"];
|
|
4775
|
+
var MAX_LISTED_UNRESOLVED_FILES = 20;
|
|
4776
|
+
async function findUnresolvedHomeFiles(cwd) {
|
|
4777
|
+
const raw = await gitExec(
|
|
4778
|
+
["ls-files", "--others", "--ignored", "--exclude-standard", "-z", "--", "home"],
|
|
4779
|
+
{ cwd }
|
|
4780
|
+
);
|
|
4781
|
+
const allPaths = raw.split("\0").filter((path) => path && !NEVER_REPORT_PREFIXES.some((prefix) => path.startsWith(prefix)));
|
|
4782
|
+
const files = [];
|
|
4783
|
+
let totalBytes = 0;
|
|
4784
|
+
for (const path of allPaths) {
|
|
4785
|
+
let bytes = -1;
|
|
4786
|
+
try {
|
|
4787
|
+
bytes = statSync3(resolve11(cwd, path)).size;
|
|
4788
|
+
} catch {
|
|
4789
|
+
}
|
|
4790
|
+
if (bytes > 0) totalBytes += bytes;
|
|
4791
|
+
if (files.length < MAX_LISTED_UNRESOLVED_FILES) files.push({ path, bytes });
|
|
4792
|
+
}
|
|
4793
|
+
return { files, totalCount: allPaths.length, totalBytes };
|
|
4794
|
+
}
|
|
4795
|
+
function formatBytes(bytes) {
|
|
4796
|
+
if (bytes < 0) return "size unknown";
|
|
4797
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
4798
|
+
const units = ["KB", "MB", "GB", "TB"];
|
|
4799
|
+
let value = bytes / 1024;
|
|
4800
|
+
for (const unit of units) {
|
|
4801
|
+
if (value < 1024) return `${value.toFixed(1)} ${unit}`;
|
|
4802
|
+
value /= 1024;
|
|
4803
|
+
}
|
|
4804
|
+
return `${value.toFixed(1)} PB`;
|
|
4805
|
+
}
|
|
4806
|
+
function formatUnresolvedHomeFilesMessage(variantName, result) {
|
|
4807
|
+
const lines = result.files.map(
|
|
4808
|
+
(f) => ` - ${f.path.replace(/^home\//, "")} (${formatBytes(f.bytes)})`
|
|
4809
|
+
);
|
|
4810
|
+
const remaining = result.totalCount - result.files.length;
|
|
4811
|
+
if (remaining > 0) lines.push(` ...and ${remaining} more`);
|
|
4812
|
+
return `Cannot join variant "${variantName}": it has ${result.totalCount} untracked file${result.totalCount === 1 ? "" : "s"} under home/ that .gitignore is hiding from git \u2014 never committed, and joining would destroy them when the variant is cleaned up (total: ${formatBytes(result.totalBytes)}):
|
|
4813
|
+
|
|
4814
|
+
${lines.join("\n")}
|
|
4815
|
+
|
|
4816
|
+
Before retrying the join, resolve each file in the variant \u2014 one of:
|
|
4817
|
+
1. Track it: git add -f -- <path> (then commit it, or let auto-commit pick it up next turn)
|
|
4818
|
+
2. Copy it out of home/ to somewhere safe, then delete it
|
|
4819
|
+
3. Delete it if it's disposable
|
|
4820
|
+
|
|
4821
|
+
Once resolved, retry the join. To join anyway and discard everything listed above, pass discardUnresolved: true.`;
|
|
4822
|
+
}
|
|
4690
4823
|
async function mergeVariantExcludingMemory(projectRoot, variantBranch) {
|
|
4691
4824
|
const opts = { cwd: projectRoot };
|
|
4692
4825
|
let memoryDelta = "";
|
|
@@ -4889,7 +5022,7 @@ async function waitForImagegenJob(mind, id, timeoutMs) {
|
|
|
4889
5022
|
import { randomUUID } from "crypto";
|
|
4890
5023
|
import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
4891
5024
|
import { homedir } from "os";
|
|
4892
|
-
import { resolve as
|
|
5025
|
+
import { resolve as resolve12 } from "path";
|
|
4893
5026
|
function convertSession(opts) {
|
|
4894
5027
|
const lines = readFileSync8(opts.sessionPath, "utf-8").trim().split("\n");
|
|
4895
5028
|
const sessionId = randomUUID();
|
|
@@ -5005,9 +5138,9 @@ function convertSession(opts) {
|
|
|
5005
5138
|
}
|
|
5006
5139
|
}
|
|
5007
5140
|
const projectId = opts.projectDir.replace(/\//g, "-");
|
|
5008
|
-
const sdkDir =
|
|
5141
|
+
const sdkDir = resolve12(homedir(), ".claude", "projects", projectId);
|
|
5009
5142
|
mkdirSync5(sdkDir, { recursive: true });
|
|
5010
|
-
const sdkPath =
|
|
5143
|
+
const sdkPath = resolve12(sdkDir, `${sessionId}.jsonl`);
|
|
5011
5144
|
writeFileSync7(sdkPath, `${sdkEvents.join("\n")}
|
|
5012
5145
|
`);
|
|
5013
5146
|
console.log(`Converted ${sdkEvents.length} messages \u2192 ${sdkPath}`);
|
|
@@ -5059,12 +5192,6 @@ function convertAssistantContent(content) {
|
|
|
5059
5192
|
return result;
|
|
5060
5193
|
}
|
|
5061
5194
|
|
|
5062
|
-
// packages/daemon/src/lib/util/time.ts
|
|
5063
|
-
function parseDbTimestamp(ts) {
|
|
5064
|
-
if (/[zZ]$|[+-]\d\d:?\d\d$/.test(ts)) return new Date(ts);
|
|
5065
|
-
return /* @__PURE__ */ new Date(`${ts.replace(" ", "T")}Z`);
|
|
5066
|
-
}
|
|
5067
|
-
|
|
5068
5195
|
// packages/daemon/src/web/api/minds.ts
|
|
5069
5196
|
var _lastActiveCache = { map: /* @__PURE__ */ new Map(), ts: 0 };
|
|
5070
5197
|
var _LAST_ACTIVE_TTL = 6e4;
|
|
@@ -5074,7 +5201,7 @@ async function getMindStatus(name, port, registryRunning, seed) {
|
|
|
5074
5201
|
let status = "stopped";
|
|
5075
5202
|
let wakeAt = null;
|
|
5076
5203
|
try {
|
|
5077
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5204
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
|
|
5078
5205
|
const sleepManager = getSleepManagerIfReady2();
|
|
5079
5206
|
if (sleepManager?.isSleeping(name)) {
|
|
5080
5207
|
status = "sleeping";
|
|
@@ -5100,10 +5227,12 @@ async function getMindStatus(name, port, registryRunning, seed) {
|
|
|
5100
5227
|
}
|
|
5101
5228
|
const lastError = await latestFailureEvent(name);
|
|
5102
5229
|
const seedChecklist = seed?.stage === "seed" ? evaluateSeedChecklist(seed.dir ?? mindDir(name)) : void 0;
|
|
5230
|
+
const memory = getMemoryStatus(seed?.dir ?? mindDir(name));
|
|
5103
5231
|
return {
|
|
5104
5232
|
status,
|
|
5105
5233
|
wakeAt,
|
|
5106
5234
|
lastError,
|
|
5235
|
+
memory,
|
|
5107
5236
|
channels: channels2,
|
|
5108
5237
|
displayName: config2?.profile?.displayName,
|
|
5109
5238
|
description: config2?.profile?.description,
|
|
@@ -5126,6 +5255,7 @@ function toPublicMind(entry, status, extras) {
|
|
|
5126
5255
|
// Kind/reason/at only — `detail` can embed the raw error string (unknown
|
|
5127
5256
|
// classifications), which is mind-private (served behind requireSelf).
|
|
5128
5257
|
lastError: status.lastError ? { kind: status.lastError.kind, reason: status.lastError.reason, at: status.lastError.at } : null,
|
|
5258
|
+
memory: status.memory,
|
|
5129
5259
|
channels: status.channels,
|
|
5130
5260
|
displayName: status.displayName,
|
|
5131
5261
|
description: status.description,
|
|
@@ -5153,7 +5283,7 @@ async function initTemplateBranch(projectRoot, composedDir, manifest, mindName,
|
|
|
5153
5283
|
await gitExec(["commit", "-m", "initial commit"], opts);
|
|
5154
5284
|
}
|
|
5155
5285
|
async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
5156
|
-
const tempWorktree =
|
|
5286
|
+
const tempWorktree = resolve13(projectRoot, ".variants", "_template_update");
|
|
5157
5287
|
let branchExists = false;
|
|
5158
5288
|
try {
|
|
5159
5289
|
await gitExec(["rev-parse", "--verify", TEMPLATE_BRANCH], { cwd: projectRoot });
|
|
@@ -5185,15 +5315,15 @@ async function updateTemplateBranch(projectRoot, template, mindName) {
|
|
|
5185
5315
|
});
|
|
5186
5316
|
}
|
|
5187
5317
|
copyTemplateToDir(composedDir, tempWorktree, mindName, manifest);
|
|
5188
|
-
const initDir =
|
|
5318
|
+
const initDir = resolve13(tempWorktree, ".init");
|
|
5189
5319
|
if (existsSync8(initDir)) {
|
|
5190
5320
|
rmSync3(initDir, { recursive: true, force: true });
|
|
5191
5321
|
}
|
|
5192
|
-
const homeDir =
|
|
5322
|
+
const homeDir = resolve13(tempWorktree, "home");
|
|
5193
5323
|
if (existsSync8(homeDir)) {
|
|
5194
5324
|
for (const entry of readdirSync2(homeDir)) {
|
|
5195
5325
|
if (entry !== "VOLUTE.md") {
|
|
5196
|
-
rmSync3(
|
|
5326
|
+
rmSync3(resolve13(homeDir, entry), { recursive: true, force: true });
|
|
5197
5327
|
}
|
|
5198
5328
|
}
|
|
5199
5329
|
}
|
|
@@ -5252,7 +5382,7 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
|
|
|
5252
5382
|
let switchWarning;
|
|
5253
5383
|
if (templateChanged) {
|
|
5254
5384
|
try {
|
|
5255
|
-
applyTemplateHomeFiles(
|
|
5385
|
+
applyTemplateHomeFiles(resolve13(dir, "home"), template);
|
|
5256
5386
|
const migratedSkills = migrateSkillsToTemplate(dir, oldTemplate, template);
|
|
5257
5387
|
await gitExec(["add", "home/"], { cwd: dir });
|
|
5258
5388
|
try {
|
|
@@ -5308,7 +5438,7 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
|
|
|
5308
5438
|
return { ok: true, warning: switchWarning };
|
|
5309
5439
|
}
|
|
5310
5440
|
async function importFromArchive(c, tempDir, nameOverride, manifest) {
|
|
5311
|
-
const extractedMindDir =
|
|
5441
|
+
const extractedMindDir = resolve13(tempDir, "mind");
|
|
5312
5442
|
if (!existsSync8(extractedMindDir)) {
|
|
5313
5443
|
return c.json({ error: "Invalid archive: missing mind/ directory" }, 400);
|
|
5314
5444
|
}
|
|
@@ -5335,9 +5465,9 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
|
|
|
5335
5465
|
}
|
|
5336
5466
|
const state = stateDir(name);
|
|
5337
5467
|
mkdirSync6(state, { recursive: true });
|
|
5338
|
-
const envJson =
|
|
5468
|
+
const envJson = resolve13(tempDir, "state/env.json");
|
|
5339
5469
|
if (existsSync8(envJson)) {
|
|
5340
|
-
cpSync(envJson,
|
|
5470
|
+
cpSync(envJson, resolve13(state, "env.json"));
|
|
5341
5471
|
}
|
|
5342
5472
|
const port = await nextPort();
|
|
5343
5473
|
await addMind(name, port, manifest.stage, manifest.template);
|
|
@@ -5346,23 +5476,23 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
|
|
|
5346
5476
|
} catch (err) {
|
|
5347
5477
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
5348
5478
|
}
|
|
5349
|
-
const homeDir =
|
|
5479
|
+
const homeDir = resolve13(dest, "home");
|
|
5350
5480
|
ensureVoluteGroup();
|
|
5351
5481
|
createMindUser(name, homeDir);
|
|
5352
5482
|
await chownMindDir(dest, name);
|
|
5353
5483
|
await npmInstallAsMind(dest, name);
|
|
5354
5484
|
await importHistoryFromArchive(name, tempDir);
|
|
5355
5485
|
importSessionsFromArchive(dest, tempDir);
|
|
5356
|
-
if (!existsSync8(
|
|
5486
|
+
if (!existsSync8(resolve13(dest, ".git"))) {
|
|
5357
5487
|
try {
|
|
5358
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
5488
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve13(dest, "home") } : void 0;
|
|
5359
5489
|
await gitExec(["init"], { cwd: dest, mindName: name, env });
|
|
5360
5490
|
await configureGitIdentity(name, { cwd: dest, mindName: name, env });
|
|
5361
5491
|
await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
|
|
5362
5492
|
await gitExec(["commit", "-m", "import from archive"], { cwd: dest, mindName: name, env });
|
|
5363
5493
|
} catch (err) {
|
|
5364
5494
|
logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
|
|
5365
|
-
rmSync3(
|
|
5495
|
+
rmSync3(resolve13(dest, ".git"), { recursive: true, force: true });
|
|
5366
5496
|
}
|
|
5367
5497
|
}
|
|
5368
5498
|
await chownMindDir(dest, name);
|
|
@@ -5395,22 +5525,22 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
5395
5525
|
try {
|
|
5396
5526
|
copyTemplateToDir(composedDir, dest, name, templateManifest);
|
|
5397
5527
|
applyInitFiles(dest);
|
|
5398
|
-
const extractedHome =
|
|
5528
|
+
const extractedHome = resolve13(extractedMindDir, "home");
|
|
5399
5529
|
if (existsSync8(extractedHome)) {
|
|
5400
|
-
cpSync(extractedHome,
|
|
5530
|
+
cpSync(extractedHome, resolve13(dest, "home"), { recursive: true });
|
|
5401
5531
|
}
|
|
5402
|
-
const extractedMindInternal =
|
|
5532
|
+
const extractedMindInternal = resolve13(extractedMindDir, ".mind");
|
|
5403
5533
|
if (existsSync8(extractedMindInternal)) {
|
|
5404
|
-
cpSync(extractedMindInternal,
|
|
5534
|
+
cpSync(extractedMindInternal, resolve13(dest, ".mind"), { recursive: true });
|
|
5405
5535
|
}
|
|
5406
|
-
const identityDir =
|
|
5536
|
+
const identityDir = resolve13(dest, ".mind/identity");
|
|
5407
5537
|
let publicKeyPem;
|
|
5408
|
-
if (!manifest.includes.identity || !existsSync8(
|
|
5538
|
+
if (!manifest.includes.identity || !existsSync8(resolve13(identityDir, "private.pem"))) {
|
|
5409
5539
|
({ publicKeyPem } = generateIdentity(dest));
|
|
5410
5540
|
} else {
|
|
5411
|
-
publicKeyPem = readFileSync9(
|
|
5541
|
+
publicKeyPem = readFileSync9(resolve13(identityDir, "public.pem"), "utf-8");
|
|
5412
5542
|
}
|
|
5413
|
-
const promptsPath =
|
|
5543
|
+
const promptsPath = resolve13(dest, "home/.config/prompts.json");
|
|
5414
5544
|
if (!existsSync8(promptsPath)) {
|
|
5415
5545
|
const mindPrompts = await getMindPromptDefaults();
|
|
5416
5546
|
writeFileSync8(promptsPath, `${JSON.stringify(mindPrompts, null, 2)}
|
|
@@ -5418,13 +5548,13 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
5418
5548
|
}
|
|
5419
5549
|
const state = stateDir(name);
|
|
5420
5550
|
mkdirSync6(state, { recursive: true });
|
|
5421
|
-
const envJson =
|
|
5551
|
+
const envJson = resolve13(tempDir, "state/env.json");
|
|
5422
5552
|
if (existsSync8(envJson)) {
|
|
5423
|
-
cpSync(envJson,
|
|
5553
|
+
cpSync(envJson, resolve13(state, "env.json"));
|
|
5424
5554
|
}
|
|
5425
5555
|
const port = await nextPort();
|
|
5426
5556
|
await addMind(name, port, manifest.stage, manifest.template);
|
|
5427
|
-
const homeDir =
|
|
5557
|
+
const homeDir = resolve13(dest, "home");
|
|
5428
5558
|
ensureVoluteGroup();
|
|
5429
5559
|
createMindUser(name, homeDir);
|
|
5430
5560
|
await chownMindDir(dest, name);
|
|
@@ -5437,7 +5567,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
5437
5567
|
await initTemplateBranch(dest, composedDir, templateManifest, name, env);
|
|
5438
5568
|
} catch (err) {
|
|
5439
5569
|
logger_default.error(`git setup failed for imported mind ${name}`, logger_default.errorData(err));
|
|
5440
|
-
rmSync3(
|
|
5570
|
+
rmSync3(resolve13(dest, ".git"), { recursive: true, force: true });
|
|
5441
5571
|
gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
|
|
5442
5572
|
}
|
|
5443
5573
|
const skillSet = manifest.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions();
|
|
@@ -5480,7 +5610,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
|
|
|
5480
5610
|
}
|
|
5481
5611
|
}
|
|
5482
5612
|
async function importHistoryFromArchive(name, tempDir) {
|
|
5483
|
-
const historyJsonl =
|
|
5613
|
+
const historyJsonl = resolve13(tempDir, "history.jsonl");
|
|
5484
5614
|
if (!existsSync8(historyJsonl)) return;
|
|
5485
5615
|
try {
|
|
5486
5616
|
const db = await getDb();
|
|
@@ -5521,13 +5651,13 @@ async function importHistoryFromArchive(name, tempDir) {
|
|
|
5521
5651
|
}
|
|
5522
5652
|
}
|
|
5523
5653
|
function importSessionsFromArchive(dest, tempDir) {
|
|
5524
|
-
const sessionsDir =
|
|
5654
|
+
const sessionsDir = resolve13(tempDir, "sessions");
|
|
5525
5655
|
if (!existsSync8(sessionsDir)) return;
|
|
5526
5656
|
try {
|
|
5527
|
-
const destSessions =
|
|
5657
|
+
const destSessions = resolve13(dest, ".mind/sessions");
|
|
5528
5658
|
mkdirSync6(destSessions, { recursive: true });
|
|
5529
5659
|
for (const file of readdirSync2(sessionsDir)) {
|
|
5530
|
-
cpSync(
|
|
5660
|
+
cpSync(resolve13(sessionsDir, file), resolve13(destSessions, file));
|
|
5531
5661
|
}
|
|
5532
5662
|
} catch (err) {
|
|
5533
5663
|
logger_default.error("Failed to import sessions from archive", logger_default.errorData(err));
|
|
@@ -5644,7 +5774,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
|
|
|
5644
5774
|
writeVoluteConfig(dest, config2);
|
|
5645
5775
|
const modelId = body.model ?? cog?.model;
|
|
5646
5776
|
effectiveModel = modelId ? qualifyModelId(modelId) : void 0;
|
|
5647
|
-
const sdkConfigPath =
|
|
5777
|
+
const sdkConfigPath = resolve13(dest, "home/.config/config.json");
|
|
5648
5778
|
if (modelId || cog?.compaction) {
|
|
5649
5779
|
const existing = existsSync8(sdkConfigPath) ? JSON.parse(readFileSync9(sdkConfigPath, "utf-8")) : {};
|
|
5650
5780
|
if (modelId) {
|
|
@@ -5659,7 +5789,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
|
|
|
5659
5789
|
}
|
|
5660
5790
|
const mindPrompts = await getMindPromptDefaults();
|
|
5661
5791
|
writeFileSync8(
|
|
5662
|
-
|
|
5792
|
+
resolve13(dest, "home/.config/prompts.json"),
|
|
5663
5793
|
`${JSON.stringify(mindPrompts, null, 2)}
|
|
5664
5794
|
`
|
|
5665
5795
|
);
|
|
@@ -5671,7 +5801,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
|
|
|
5671
5801
|
} catch (err) {
|
|
5672
5802
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
5673
5803
|
}
|
|
5674
|
-
const homeDir =
|
|
5804
|
+
const homeDir = resolve13(dest, "home");
|
|
5675
5805
|
ensureVoluteGroup();
|
|
5676
5806
|
createMindUser(name, homeDir);
|
|
5677
5807
|
await chownMindDir(dest, name);
|
|
@@ -5684,7 +5814,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
|
|
|
5684
5814
|
await initTemplateBranch(dest, composedDir, manifest, name, env);
|
|
5685
5815
|
} catch (err) {
|
|
5686
5816
|
logger_default.error(`git setup failed for ${name}`, logger_default.errorData(err));
|
|
5687
|
-
rmSync3(
|
|
5817
|
+
rmSync3(resolve13(dest, ".git"), { recursive: true, force: true });
|
|
5688
5818
|
gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
|
|
5689
5819
|
}
|
|
5690
5820
|
if (body.stage === "seed") {
|
|
@@ -5693,7 +5823,7 @@ Your creator described you as: "${body.description}"
|
|
|
5693
5823
|
` : "";
|
|
5694
5824
|
const seedSoulRaw = body.seedSoul ?? await getPrompt("seed_soul", { name, description: descLine });
|
|
5695
5825
|
const seedSoul = body.seedSoul ? substitute(seedSoulRaw, { name, description: descLine }) : seedSoulRaw;
|
|
5696
|
-
writeFileSync8(
|
|
5826
|
+
writeFileSync8(resolve13(dest, "home/SOUL.md"), seedSoul);
|
|
5697
5827
|
}
|
|
5698
5828
|
let skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
|
|
5699
5829
|
if (body.stage === "seed" && !body.skills) {
|
|
@@ -5719,7 +5849,7 @@ Your creator described you as: "${body.description}"
|
|
|
5719
5849
|
const spiritName = getSpiritName();
|
|
5720
5850
|
const spiritEntry = await findMind(spiritName);
|
|
5721
5851
|
if (spiritEntry) {
|
|
5722
|
-
const { spiritDir } = await import("./spirit-
|
|
5852
|
+
const { spiritDir } = await import("./spirit-I5ECLGRI.js");
|
|
5723
5853
|
const sDir = spiritEntry.dir ?? spiritDir();
|
|
5724
5854
|
const spiritConfig = readVoluteConfig(sDir) ?? {};
|
|
5725
5855
|
const schedules = spiritConfig.schedules ?? [];
|
|
@@ -5734,7 +5864,7 @@ Your creator described you as: "${body.description}"
|
|
|
5734
5864
|
});
|
|
5735
5865
|
spiritConfig.schedules = schedules;
|
|
5736
5866
|
writeVoluteConfig(sDir, spiritConfig);
|
|
5737
|
-
const { getScheduler: getScheduler2 } = await import("./scheduler-
|
|
5867
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-5X5UIQOQ.js");
|
|
5738
5868
|
getScheduler2().loadSchedules(spiritName, sDir);
|
|
5739
5869
|
}
|
|
5740
5870
|
}
|
|
@@ -5745,11 +5875,11 @@ Your creator described you as: "${body.description}"
|
|
|
5745
5875
|
if (body.stage !== "seed") {
|
|
5746
5876
|
const customSoul = await getPromptIfCustom("default_soul");
|
|
5747
5877
|
if (customSoul) {
|
|
5748
|
-
writeFileSync8(
|
|
5878
|
+
writeFileSync8(resolve13(dest, "home/SOUL.md"), customSoul.replace(/\{\{name\}\}/g, name));
|
|
5749
5879
|
}
|
|
5750
5880
|
const customMemory = await getPromptIfCustom("default_memory");
|
|
5751
5881
|
if (customMemory) {
|
|
5752
|
-
writeFileSync8(
|
|
5882
|
+
writeFileSync8(resolve13(dest, "home/MEMORY.md"), customMemory);
|
|
5753
5883
|
}
|
|
5754
5884
|
}
|
|
5755
5885
|
await chownMindDir(dest, name);
|
|
@@ -5810,12 +5940,12 @@ Your creator described you as: "${body.description}"
|
|
|
5810
5940
|
return importFromArchive(c, body.archivePath, body.name, body.manifest);
|
|
5811
5941
|
}
|
|
5812
5942
|
const wsDir = body.workspacePath;
|
|
5813
|
-
if (!wsDir || !existsSync8(
|
|
5943
|
+
if (!wsDir || !existsSync8(resolve13(wsDir, "SOUL.md")) || !existsSync8(resolve13(wsDir, "IDENTITY.md"))) {
|
|
5814
5944
|
return c.json({ error: "Invalid workspace: missing SOUL.md or IDENTITY.md" }, 400);
|
|
5815
5945
|
}
|
|
5816
|
-
const soul = readFileSync9(
|
|
5817
|
-
const identity = readFileSync9(
|
|
5818
|
-
const userPath =
|
|
5946
|
+
const soul = readFileSync9(resolve13(wsDir, "SOUL.md"), "utf-8");
|
|
5947
|
+
const identity = readFileSync9(resolve13(wsDir, "IDENTITY.md"), "utf-8");
|
|
5948
|
+
const userPath = resolve13(wsDir, "USER.md");
|
|
5819
5949
|
const user = existsSync8(userPath) ? readFileSync9(userPath, "utf-8") : "";
|
|
5820
5950
|
const name = body.name ?? parseNameFromIdentity(identity) ?? "imported-mind";
|
|
5821
5951
|
const template = body.template ?? "claude";
|
|
@@ -5843,26 +5973,26 @@ ${user.trimEnd()}
|
|
|
5843
5973
|
copyTemplateToDir(composedDir, dest, name, manifest);
|
|
5844
5974
|
applyInitFiles(dest);
|
|
5845
5975
|
const { publicKeyPem: importPublicKey } = generateIdentity(dest);
|
|
5846
|
-
writeFileSync8(
|
|
5847
|
-
const wsMemoryPath =
|
|
5976
|
+
writeFileSync8(resolve13(dest, "home/SOUL.md"), mergedSoul);
|
|
5977
|
+
const wsMemoryPath = resolve13(wsDir, "MEMORY.md");
|
|
5848
5978
|
const hasMemory = existsSync8(wsMemoryPath);
|
|
5849
5979
|
if (hasMemory) {
|
|
5850
5980
|
const existingMemory = readFileSync9(wsMemoryPath, "utf-8");
|
|
5851
5981
|
writeFileSync8(
|
|
5852
|
-
|
|
5982
|
+
resolve13(dest, "home/MEMORY.md"),
|
|
5853
5983
|
`${existingMemory.trimEnd()}${mergedMemoryExtra}`
|
|
5854
5984
|
);
|
|
5855
5985
|
} else if (user) {
|
|
5856
|
-
writeFileSync8(
|
|
5986
|
+
writeFileSync8(resolve13(dest, "home/MEMORY.md"), `${user.trimEnd()}
|
|
5857
5987
|
`);
|
|
5858
5988
|
}
|
|
5859
|
-
const wsMemoryDir =
|
|
5989
|
+
const wsMemoryDir = resolve13(wsDir, "memory");
|
|
5860
5990
|
let dailyLogCount = 0;
|
|
5861
5991
|
if (existsSync8(wsMemoryDir)) {
|
|
5862
|
-
const destMemoryDir =
|
|
5992
|
+
const destMemoryDir = resolve13(dest, "home/memory");
|
|
5863
5993
|
const files = readdirSync2(wsMemoryDir).filter((f) => f.endsWith(".md"));
|
|
5864
5994
|
for (const file of files) {
|
|
5865
|
-
cpSync(
|
|
5995
|
+
cpSync(resolve13(wsMemoryDir, file), resolve13(destMemoryDir, file));
|
|
5866
5996
|
}
|
|
5867
5997
|
dailyLogCount = files.length;
|
|
5868
5998
|
}
|
|
@@ -5873,7 +6003,7 @@ ${user.trimEnd()}
|
|
|
5873
6003
|
} catch (err) {
|
|
5874
6004
|
logger_default.warn(`failed to set template hash for ${name}`, logger_default.errorData(err));
|
|
5875
6005
|
}
|
|
5876
|
-
const homeDir =
|
|
6006
|
+
const homeDir = resolve13(dest, "home");
|
|
5877
6007
|
ensureVoluteGroup();
|
|
5878
6008
|
createMindUser(name, homeDir);
|
|
5879
6009
|
await chownMindDir(dest, name);
|
|
@@ -5881,20 +6011,20 @@ ${user.trimEnd()}
|
|
|
5881
6011
|
if (!hasMemory && dailyLogCount > 0) {
|
|
5882
6012
|
await consolidateMemory(dest);
|
|
5883
6013
|
}
|
|
5884
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
6014
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve13(dest, "home") } : void 0;
|
|
5885
6015
|
await gitExec(["init"], { cwd: dest, mindName: name, env });
|
|
5886
6016
|
await configureGitIdentity(name, { cwd: dest, mindName: name, env });
|
|
5887
6017
|
await gitExec(["add", "-A"], { cwd: dest, mindName: name, env });
|
|
5888
6018
|
await gitExec(["commit", "-m", "import from OpenClaw"], { cwd: dest, mindName: name, env });
|
|
5889
|
-
const sessionFile = body.sessionPath ?
|
|
6019
|
+
const sessionFile = body.sessionPath ? resolve13(body.sessionPath) : findOpenClawSession(wsDir);
|
|
5890
6020
|
if (sessionFile && existsSync8(sessionFile)) {
|
|
5891
6021
|
if (template === "pi") {
|
|
5892
6022
|
importPiSession(sessionFile, dest);
|
|
5893
6023
|
} else if (template === "claude") {
|
|
5894
6024
|
const sessionId = convertSession({ sessionPath: sessionFile, projectDir: dest });
|
|
5895
|
-
const mindRuntimeDir =
|
|
6025
|
+
const mindRuntimeDir = resolve13(dest, ".mind");
|
|
5896
6026
|
mkdirSync6(mindRuntimeDir, { recursive: true });
|
|
5897
|
-
writeFileSync8(
|
|
6027
|
+
writeFileSync8(resolve13(mindRuntimeDir, "session.json"), JSON.stringify({ sessionId }));
|
|
5898
6028
|
}
|
|
5899
6029
|
}
|
|
5900
6030
|
importOpenClawConnectors(name, dest);
|
|
@@ -5940,7 +6070,7 @@ ${user.trimEnd()}
|
|
|
5940
6070
|
stage: entry.stage,
|
|
5941
6071
|
dir: entry.dir
|
|
5942
6072
|
});
|
|
5943
|
-
const hasPages = existsSync8(
|
|
6073
|
+
const hasPages = existsSync8(resolve13(mindDir(entry.name), "home", "pages"));
|
|
5944
6074
|
const lastActiveAt = lastActiveMap.get(entry.name) ?? null;
|
|
5945
6075
|
if (!privileged) return toPublicMind(entry, mindStatus, { hasPages, lastActiveAt });
|
|
5946
6076
|
return {
|
|
@@ -5963,7 +6093,7 @@ ${user.trimEnd()}
|
|
|
5963
6093
|
stage: entry.stage,
|
|
5964
6094
|
dir: entry.dir
|
|
5965
6095
|
});
|
|
5966
|
-
const hasPages = existsSync8(
|
|
6096
|
+
const hasPages = existsSync8(resolve13(mindDir(name), "home", "pages"));
|
|
5967
6097
|
if (!isPrivileged(c)) {
|
|
5968
6098
|
return c.json(toPublicMind(entry, mindStatus, { hasPages }));
|
|
5969
6099
|
}
|
|
@@ -6040,7 +6170,7 @@ ${user.trimEnd()}
|
|
|
6040
6170
|
const manager = getMindManager();
|
|
6041
6171
|
try {
|
|
6042
6172
|
if (context?.type === "reload") {
|
|
6043
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
6173
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
|
|
6044
6174
|
const sleepState = getSleepManagerIfReady2()?.getState(name);
|
|
6045
6175
|
if (sleepState?.sleeping) {
|
|
6046
6176
|
logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
|
|
@@ -6060,6 +6190,7 @@ ${user.trimEnd()}
|
|
|
6060
6190
|
const variantEntry = await findMind(mergeVariantName);
|
|
6061
6191
|
if (variantEntry && variantEntry.parent === baseName && variantEntry.dir && variantEntry.branch) {
|
|
6062
6192
|
const projectRoot = mindDir(baseName);
|
|
6193
|
+
const discardUnresolved = context.discardUnresolvedHomeFiles === true;
|
|
6063
6194
|
if (existsSync8(variantEntry.dir)) {
|
|
6064
6195
|
const status = (await gitExec(["status", "--porcelain"], { cwd: variantEntry.dir })).trim();
|
|
6065
6196
|
if (status) {
|
|
@@ -6089,13 +6220,41 @@ ${user.trimEnd()}
|
|
|
6089
6220
|
}
|
|
6090
6221
|
const preMergeHead = (await gitExec(["rev-parse", "HEAD"], { cwd: projectRoot })).trim();
|
|
6091
6222
|
const memoryDelta = await mergeVariantExcludingMemory(projectRoot, variantEntry.branch);
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
if (await npmInstallNeeded(projectRoot, preMergeHead)) {
|
|
6223
|
+
let unresolvedNotice;
|
|
6224
|
+
if (existsSync8(variantEntry.dir) && !discardUnresolved) {
|
|
6095
6225
|
try {
|
|
6096
|
-
await
|
|
6097
|
-
|
|
6098
|
-
|
|
6226
|
+
const unresolved = await findUnresolvedHomeFiles(variantEntry.dir);
|
|
6227
|
+
if (unresolved.totalCount > 0) {
|
|
6228
|
+
unresolvedNotice = formatUnresolvedHomeFilesMessage(mergeVariantName, unresolved);
|
|
6229
|
+
}
|
|
6230
|
+
} catch (err) {
|
|
6231
|
+
unresolvedNotice = `Could not verify variant ${mergeVariantName} has no unresolved home/ files: ${err instanceof Error ? err.message : String(err)}. Variant left intact \u2014 retry the join.`;
|
|
6232
|
+
}
|
|
6233
|
+
}
|
|
6234
|
+
if (unresolvedNotice) {
|
|
6235
|
+
logger_default.warn(
|
|
6236
|
+
`variant join blocked for ${baseName}: ${mergeVariantName} \u2014 ${unresolvedNotice}`
|
|
6237
|
+
);
|
|
6238
|
+
await recordNotice({
|
|
6239
|
+
mind: baseName,
|
|
6240
|
+
thread: "main",
|
|
6241
|
+
kind: "join_blocked",
|
|
6242
|
+
reason: "unresolved_variant_files",
|
|
6243
|
+
detail: unresolvedNotice
|
|
6244
|
+
});
|
|
6245
|
+
if (context) {
|
|
6246
|
+
context.type = "restart";
|
|
6247
|
+
context.name = void 0;
|
|
6248
|
+
}
|
|
6249
|
+
} else {
|
|
6250
|
+
if (memoryDelta && context) context.memoryDelta = memoryDelta;
|
|
6251
|
+
await cleanupVariant(mergeVariantName, baseName, projectRoot, variantEntry.dir);
|
|
6252
|
+
if (await npmInstallNeeded(projectRoot, preMergeHead)) {
|
|
6253
|
+
try {
|
|
6254
|
+
await npmInstallAsMind(projectRoot, baseName);
|
|
6255
|
+
} catch (e) {
|
|
6256
|
+
logger_default.error(`npm install failed after merge for ${baseName}`, logger_default.errorData(e));
|
|
6257
|
+
}
|
|
6099
6258
|
}
|
|
6100
6259
|
}
|
|
6101
6260
|
}
|
|
@@ -6138,6 +6297,7 @@ ${user.trimEnd()}
|
|
|
6138
6297
|
);
|
|
6139
6298
|
return c.json({ ok: true, port: targetPort });
|
|
6140
6299
|
} catch (err) {
|
|
6300
|
+
logger_default.error(`failed to restart mind ${name}`, logger_default.errorData(err));
|
|
6141
6301
|
return c.json({ error: err instanceof Error ? err.message : "Failed to restart mind" }, 500);
|
|
6142
6302
|
}
|
|
6143
6303
|
}).post("/:name/stop", requireSelf(), async (c) => {
|
|
@@ -6152,13 +6312,14 @@ ${user.trimEnd()}
|
|
|
6152
6312
|
await stopMindFull(name);
|
|
6153
6313
|
return c.json({ ok: true });
|
|
6154
6314
|
} catch (err) {
|
|
6315
|
+
logger_default.error(`failed to stop mind ${name}`, logger_default.errorData(err));
|
|
6155
6316
|
return c.json({ error: err instanceof Error ? err.message : "Failed to stop mind" }, 500);
|
|
6156
6317
|
}
|
|
6157
6318
|
}).get("/:name/sleep", requireSelf(), async (c) => {
|
|
6158
6319
|
const name = c.req.param("name");
|
|
6159
6320
|
const entry = await findMind(name);
|
|
6160
6321
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
6161
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
6322
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
|
|
6162
6323
|
const sm = getSleepManagerIfReady2();
|
|
6163
6324
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
6164
6325
|
return c.json(sm.getState(name));
|
|
@@ -6166,7 +6327,7 @@ ${user.trimEnd()}
|
|
|
6166
6327
|
const name = c.req.param("name");
|
|
6167
6328
|
const entry = await findMind(name);
|
|
6168
6329
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
6169
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
6330
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
|
|
6170
6331
|
const sm = getSleepManagerIfReady2();
|
|
6171
6332
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
6172
6333
|
if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
|
|
@@ -6186,7 +6347,7 @@ ${user.trimEnd()}
|
|
|
6186
6347
|
const name = c.req.param("name");
|
|
6187
6348
|
const entry = await findMind(name);
|
|
6188
6349
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
6189
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
6350
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
|
|
6190
6351
|
const sm = getSleepManagerIfReady2();
|
|
6191
6352
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
6192
6353
|
const sleepState = sm.getState(name);
|
|
@@ -6201,7 +6362,7 @@ ${user.trimEnd()}
|
|
|
6201
6362
|
const name = c.req.param("name");
|
|
6202
6363
|
const entry = await findMind(name);
|
|
6203
6364
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
6204
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
6365
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
|
|
6205
6366
|
const sm = getSleepManagerIfReady2();
|
|
6206
6367
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
6207
6368
|
const flushed = await sm.flushQueuedMessages(name);
|
|
@@ -6217,7 +6378,7 @@ ${user.trimEnd()}
|
|
|
6217
6378
|
if (body.displayName !== void 0) profile.displayName = body.displayName;
|
|
6218
6379
|
if (body.description !== void 0) profile.description = body.description;
|
|
6219
6380
|
if (body.avatar !== void 0) {
|
|
6220
|
-
const homeDir =
|
|
6381
|
+
const homeDir = resolve13(dir, "home");
|
|
6221
6382
|
const avatarPath = safeResolveWithinBase(homeDir, body.avatar);
|
|
6222
6383
|
if (!avatarPath) {
|
|
6223
6384
|
return c.json({ error: "Avatar path must be inside the mind's home directory" }, 400);
|
|
@@ -6229,7 +6390,7 @@ ${user.trimEnd()}
|
|
|
6229
6390
|
}
|
|
6230
6391
|
config2.profile = profile;
|
|
6231
6392
|
writeVoluteConfig(dir, config2);
|
|
6232
|
-
const { syncMindProfile: syncMindProfile2 } = await import("./auth-
|
|
6393
|
+
const { syncMindProfile: syncMindProfile2 } = await import("./auth-KRDSM55Q.js");
|
|
6233
6394
|
await syncMindProfile2(name, profile);
|
|
6234
6395
|
broadcast({ type: "profile_updated", mind: name, summary: `${name} profile updated` });
|
|
6235
6396
|
return c.json({ ok: true });
|
|
@@ -6273,7 +6434,7 @@ ${user.trimEnd()}
|
|
|
6273
6434
|
});
|
|
6274
6435
|
}
|
|
6275
6436
|
if (!force) {
|
|
6276
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
6437
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-NWUCRBZB.js");
|
|
6277
6438
|
if (getSleepManagerIfReady2()?.isSleeping(name)) {
|
|
6278
6439
|
return c.json({ output: "" });
|
|
6279
6440
|
}
|
|
@@ -6328,7 +6489,7 @@ ${user.trimEnd()}
|
|
|
6328
6489
|
}
|
|
6329
6490
|
}
|
|
6330
6491
|
const dir = entry.dir ?? mindDir(name);
|
|
6331
|
-
const { evaluateSeedChecklist: evaluateSeedChecklist2 } = await import("./seed-readiness-
|
|
6492
|
+
const { evaluateSeedChecklist: evaluateSeedChecklist2 } = await import("./seed-readiness-WJYYRRCK.js");
|
|
6332
6493
|
const checklist = evaluateSeedChecklist2(dir);
|
|
6333
6494
|
const done = [];
|
|
6334
6495
|
const remaining = [];
|
|
@@ -6370,7 +6531,7 @@ ${user.trimEnd()}
|
|
|
6370
6531
|
try {
|
|
6371
6532
|
const sproutedDir = entry.dir ?? mindDir(name);
|
|
6372
6533
|
if (setupDefaultDreaming(sproutedDir).schedulesChanged) {
|
|
6373
|
-
const { getScheduler: getScheduler2 } = await import("./scheduler-
|
|
6534
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-5X5UIQOQ.js");
|
|
6374
6535
|
getScheduler2().loadSchedules(name, sproutedDir);
|
|
6375
6536
|
}
|
|
6376
6537
|
} catch (err) {
|
|
@@ -6380,7 +6541,7 @@ ${user.trimEnd()}
|
|
|
6380
6541
|
const spiritName = getSpiritName();
|
|
6381
6542
|
const spiritEntry = await findMind(spiritName);
|
|
6382
6543
|
if (spiritEntry) {
|
|
6383
|
-
const { firstWeekSchedules, spiritDir } = await import("./spirit-
|
|
6544
|
+
const { firstWeekSchedules, spiritDir } = await import("./spirit-I5ECLGRI.js");
|
|
6384
6545
|
const sDir = spiritEntry.dir ?? spiritDir();
|
|
6385
6546
|
const spiritConfig = readVoluteConfig(sDir);
|
|
6386
6547
|
if (spiritConfig) {
|
|
@@ -6394,7 +6555,7 @@ ${user.trimEnd()}
|
|
|
6394
6555
|
spiritConfig.schedules = schedules;
|
|
6395
6556
|
writeVoluteConfig(sDir, spiritConfig);
|
|
6396
6557
|
try {
|
|
6397
|
-
const { getScheduler: getScheduler2 } = await import("./scheduler-
|
|
6558
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-5X5UIQOQ.js");
|
|
6398
6559
|
getScheduler2().loadSchedules(spiritName, sDir);
|
|
6399
6560
|
} catch (err) {
|
|
6400
6561
|
logger_default.warn(
|
|
@@ -6416,9 +6577,25 @@ ${user.trimEnd()}
|
|
|
6416
6577
|
const name = c.req.param("name");
|
|
6417
6578
|
const entry = await findMind(name);
|
|
6418
6579
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
6580
|
+
const manager = getMindManager();
|
|
6581
|
+
if (entry.parent) {
|
|
6582
|
+
if (!entry.dir) return c.json({ error: `Variant ${name} has no directory` }, 500);
|
|
6583
|
+
const parentEntry = await findMind(entry.parent);
|
|
6584
|
+
if (!parentEntry) return c.json({ error: `Parent mind ${entry.parent} not found` }, 404);
|
|
6585
|
+
if (manager.isRunning(name)) {
|
|
6586
|
+
await stopMindFull(name);
|
|
6587
|
+
}
|
|
6588
|
+
await cleanupVariant(name, entry.parent, parentEntry.dir ?? mindDir(entry.parent), entry.dir);
|
|
6589
|
+
invalidateMindUserCache(name);
|
|
6590
|
+
fireWebhook({
|
|
6591
|
+
event: "mind_deleted",
|
|
6592
|
+
mind: name,
|
|
6593
|
+
data: { port: entry.port, stage: entry.stage, template: entry.template }
|
|
6594
|
+
});
|
|
6595
|
+
return c.json({ ok: true, variant: true });
|
|
6596
|
+
}
|
|
6419
6597
|
const dir = mindDir(name);
|
|
6420
6598
|
const force = c.req.query("force") === "true";
|
|
6421
|
-
const manager = getMindManager();
|
|
6422
6599
|
if (manager.isRunning(name)) {
|
|
6423
6600
|
await stopMindFull(name);
|
|
6424
6601
|
}
|
|
@@ -6463,16 +6640,16 @@ ${user.trimEnd()}
|
|
|
6463
6640
|
}
|
|
6464
6641
|
const UPGRADE_BRANCH = "upgrade";
|
|
6465
6642
|
const upgradeVariantName = `${mindName}-upgrade`;
|
|
6466
|
-
const worktreeDir =
|
|
6643
|
+
const worktreeDir = resolve13(dir, ".variants", UPGRADE_BRANCH);
|
|
6467
6644
|
if (body.abort) {
|
|
6468
6645
|
if (!existsSync8(worktreeDir)) {
|
|
6469
6646
|
return c.json({ error: "No upgrade in progress" }, 400);
|
|
6470
6647
|
}
|
|
6471
6648
|
try {
|
|
6472
6649
|
try {
|
|
6473
|
-
const gitDirContent = readFileSync9(
|
|
6650
|
+
const gitDirContent = readFileSync9(resolve13(worktreeDir, ".git"), "utf-8").trim();
|
|
6474
6651
|
const gitDir = gitDirContent.replace("gitdir: ", "");
|
|
6475
|
-
if (existsSync8(
|
|
6652
|
+
if (existsSync8(resolve13(gitDir, "MERGE_HEAD"))) {
|
|
6476
6653
|
await gitExec(["merge", "--abort"], { cwd: worktreeDir });
|
|
6477
6654
|
}
|
|
6478
6655
|
} catch {
|
|
@@ -6556,7 +6733,7 @@ ${user.trimEnd()}
|
|
|
6556
6733
|
}
|
|
6557
6734
|
if (body.diff) {
|
|
6558
6735
|
try {
|
|
6559
|
-
if (!existsSync8(
|
|
6736
|
+
if (!existsSync8(resolve13(dir, ".git"))) {
|
|
6560
6737
|
return c.json({ error: "Mind has no git history \u2014 nothing to diff against" }, 400);
|
|
6561
6738
|
}
|
|
6562
6739
|
await updateTemplateBranch(dir, template, mindName);
|
|
@@ -6580,16 +6757,16 @@ ${user.trimEnd()}
|
|
|
6580
6757
|
409
|
|
6581
6758
|
);
|
|
6582
6759
|
}
|
|
6583
|
-
if (!existsSync8(
|
|
6760
|
+
if (!existsSync8(resolve13(dir, ".git"))) {
|
|
6584
6761
|
try {
|
|
6585
|
-
const env = isIsolationEnabled() ? { ...process.env, HOME:
|
|
6762
|
+
const env = isIsolationEnabled() ? { ...process.env, HOME: resolve13(dir, "home") } : void 0;
|
|
6586
6763
|
await gitExec(["init"], { cwd: dir, mindName, env });
|
|
6587
6764
|
await configureGitIdentity(mindName, { cwd: dir, mindName, env });
|
|
6588
6765
|
await gitExec(["add", "-A"], { cwd: dir, mindName, env });
|
|
6589
6766
|
await gitExec(["commit", "-m", "initial commit"], { cwd: dir, mindName, env });
|
|
6590
6767
|
await chownMindDir(dir, mindName);
|
|
6591
6768
|
} catch (err) {
|
|
6592
|
-
rmSync3(
|
|
6769
|
+
rmSync3(resolve13(dir, ".git"), { recursive: true, force: true });
|
|
6593
6770
|
return c.json(
|
|
6594
6771
|
{
|
|
6595
6772
|
error: `Git initialization failed: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -6604,7 +6781,7 @@ ${user.trimEnd()}
|
|
|
6604
6781
|
} catch {
|
|
6605
6782
|
}
|
|
6606
6783
|
await updateTemplateBranch(dir, template, mindName);
|
|
6607
|
-
const parentDir =
|
|
6784
|
+
const parentDir = resolve13(dir, ".variants");
|
|
6608
6785
|
if (!existsSync8(parentDir)) {
|
|
6609
6786
|
mkdirSync6(parentDir, { recursive: true });
|
|
6610
6787
|
}
|
|
@@ -6741,7 +6918,7 @@ ${user.trimEnd()}
|
|
|
6741
6918
|
if (!existsSync8(dir)) return c.json({ error: "Mind directory missing" }, 404);
|
|
6742
6919
|
let config2 = readVoluteConfig(dir);
|
|
6743
6920
|
if (!config2 && entry.template === "pi") {
|
|
6744
|
-
const piConfigPath =
|
|
6921
|
+
const piConfigPath = resolve13(dir, "home/.config/config.json");
|
|
6745
6922
|
if (existsSync8(piConfigPath)) {
|
|
6746
6923
|
try {
|
|
6747
6924
|
config2 = JSON.parse(readFileSync9(piConfigPath, "utf-8"));
|
|
@@ -6750,7 +6927,7 @@ ${user.trimEnd()}
|
|
|
6750
6927
|
}
|
|
6751
6928
|
}
|
|
6752
6929
|
let templateConfig = {};
|
|
6753
|
-
const configJsonPath =
|
|
6930
|
+
const configJsonPath = resolve13(dir, "home/.config/config.json");
|
|
6754
6931
|
if (existsSync8(configJsonPath)) {
|
|
6755
6932
|
try {
|
|
6756
6933
|
templateConfig = JSON.parse(readFileSync9(configJsonPath, "utf-8"));
|
|
@@ -6819,12 +6996,12 @@ ${user.trimEnd()}
|
|
|
6819
6996
|
}
|
|
6820
6997
|
writeVoluteConfig(dir, existing);
|
|
6821
6998
|
if (body.unescapeNewlines !== void 0) {
|
|
6822
|
-
const { clearEchoTextCache } = await import("./echo-text-
|
|
6999
|
+
const { clearEchoTextCache } = await import("./echo-text-KU6G4Z7V.js");
|
|
6823
7000
|
clearEchoTextCache(name);
|
|
6824
7001
|
}
|
|
6825
7002
|
const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.compaction !== void 0;
|
|
6826
7003
|
if (needsConfigJson) {
|
|
6827
|
-
const configJsonPath =
|
|
7004
|
+
const configJsonPath = resolve13(dir, "home/.config/config.json");
|
|
6828
7005
|
let templateConfig = {};
|
|
6829
7006
|
if (existsSync8(configJsonPath)) {
|
|
6830
7007
|
try {
|
|
@@ -7144,6 +7321,35 @@ ${lines.join("\n")}` });
|
|
|
7144
7321
|
const maxId = notices.reduce((m, n) => Math.max(m, n.id), 0);
|
|
7145
7322
|
setNoticeDrainWatermark(baseName, session, maxId);
|
|
7146
7323
|
return c.json({ context: formatEvents(notices), notices });
|
|
7324
|
+
}).post("/:name/notices", requireSelf(), async (c) => {
|
|
7325
|
+
const MIND_POSTABLE_KINDS = ["context_lost", "delivery_failed"];
|
|
7326
|
+
const name = c.req.param("name");
|
|
7327
|
+
const baseName = await getBaseName(name);
|
|
7328
|
+
let body;
|
|
7329
|
+
try {
|
|
7330
|
+
body = await c.req.json();
|
|
7331
|
+
} catch {
|
|
7332
|
+
return c.json({ error: "Invalid JSON body" }, 400);
|
|
7333
|
+
}
|
|
7334
|
+
const kind = body.kind;
|
|
7335
|
+
if (typeof kind !== "string" || !MIND_POSTABLE_KINDS.includes(kind)) {
|
|
7336
|
+
return c.json({ error: `kind must be one of: ${MIND_POSTABLE_KINDS.join(", ")}` }, 400);
|
|
7337
|
+
}
|
|
7338
|
+
const message = typeof body.message === "string" ? body.message.trim() : "";
|
|
7339
|
+
if (!message) return c.json({ error: "message is required" }, 400);
|
|
7340
|
+
if (body.thread !== void 0 && typeof body.thread !== "string") {
|
|
7341
|
+
return c.json({ error: "thread must be a string" }, 400);
|
|
7342
|
+
}
|
|
7343
|
+
await recordNotice({
|
|
7344
|
+
mind: baseName,
|
|
7345
|
+
thread: body.thread ?? MIND_LEVEL_THREAD,
|
|
7346
|
+
kind,
|
|
7347
|
+
reason: kind,
|
|
7348
|
+
// Bound the size (the body is injected into turn context) without dropping
|
|
7349
|
+
// the notice — a truncated explanation beats a silent 400.
|
|
7350
|
+
detail: message.length > 4e3 ? `${message.slice(0, 4e3)}\u2026` : message
|
|
7351
|
+
});
|
|
7352
|
+
return c.json({ ok: true });
|
|
7147
7353
|
}).get("/:name/system-events", requireSelf(), async (c) => {
|
|
7148
7354
|
const name = c.req.param("name");
|
|
7149
7355
|
const baseName = await getBaseName(name);
|
|
@@ -7166,6 +7372,43 @@ ${lines.join("\n")}` });
|
|
|
7166
7372
|
};
|
|
7167
7373
|
})
|
|
7168
7374
|
});
|
|
7375
|
+
}).put("/:name/turn-summaries", requireSelf(), async (c) => {
|
|
7376
|
+
const name = c.req.param("name");
|
|
7377
|
+
const baseName = await getBaseName(name);
|
|
7378
|
+
let body;
|
|
7379
|
+
try {
|
|
7380
|
+
body = await c.req.json();
|
|
7381
|
+
} catch {
|
|
7382
|
+
return c.json({ error: "Invalid JSON body" }, 400);
|
|
7383
|
+
}
|
|
7384
|
+
const items = body.summaries;
|
|
7385
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
7386
|
+
return c.json({ error: "summaries must be a non-empty array" }, 400);
|
|
7387
|
+
}
|
|
7388
|
+
let updated = 0;
|
|
7389
|
+
let created = 0;
|
|
7390
|
+
for (const item of items) {
|
|
7391
|
+
if (!item || typeof item.turnId !== "string" || !item.turnId) {
|
|
7392
|
+
return c.json({ error: "each summary needs a turnId" }, 400);
|
|
7393
|
+
}
|
|
7394
|
+
if (typeof item.content !== "string") {
|
|
7395
|
+
return c.json({ error: "each summary needs content" }, 400);
|
|
7396
|
+
}
|
|
7397
|
+
const result = await supersedeTurnSummary(baseName, item.turnId, item.content);
|
|
7398
|
+
switch (result.status) {
|
|
7399
|
+
case "invalid":
|
|
7400
|
+
return c.json({ error: result.error }, 400);
|
|
7401
|
+
case "not_found":
|
|
7402
|
+
return c.json({ error: "Turn not found" }, 404);
|
|
7403
|
+
case "forbidden":
|
|
7404
|
+
return c.json({ error: "Forbidden" }, 403);
|
|
7405
|
+
case "ok":
|
|
7406
|
+
if (result.created) created++;
|
|
7407
|
+
else updated++;
|
|
7408
|
+
break;
|
|
7409
|
+
}
|
|
7410
|
+
}
|
|
7411
|
+
return c.json({ ok: true, updated, created });
|
|
7169
7412
|
}).get("/:name/history", requireSelf(), async (c) => {
|
|
7170
7413
|
const name = c.req.param("name");
|
|
7171
7414
|
const channel = c.req.query("channel");
|
|
@@ -7174,6 +7417,7 @@ ${lines.join("\n")}` });
|
|
|
7174
7417
|
const preset = c.req.query("preset");
|
|
7175
7418
|
const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
|
|
7176
7419
|
const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
|
|
7420
|
+
const provisional = c.req.query("provisional") === "true";
|
|
7177
7421
|
const db = await getDb();
|
|
7178
7422
|
const conditions = [eq5(mindHistory.mind, name)];
|
|
7179
7423
|
if (channel) {
|
|
@@ -7185,6 +7429,9 @@ ${lines.join("\n")}` });
|
|
|
7185
7429
|
const effectivePreset = full ? "all" : preset;
|
|
7186
7430
|
if (!effectivePreset || effectivePreset === "summary") {
|
|
7187
7431
|
const sumConditions = [eq5(summaries.mind, name), eq5(summaries.period, "turn")];
|
|
7432
|
+
if (provisional) {
|
|
7433
|
+
sumConditions.push(sql3`json_extract(${summaries.metadata}, '$.author') is not ${"mind"}`);
|
|
7434
|
+
}
|
|
7188
7435
|
if (session) {
|
|
7189
7436
|
sumConditions.push(eq5(turns.thread, session));
|
|
7190
7437
|
const sumRows2 = await db.select({
|
|
@@ -7593,7 +7840,7 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
|
|
|
7593
7840
|
const entry = await findMind(name);
|
|
7594
7841
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
7595
7842
|
const body = await c.req.text();
|
|
7596
|
-
const { deliverEvent: deliverEvent2 } = await import("./system-events-
|
|
7843
|
+
const { deliverEvent: deliverEvent2 } = await import("./system-events-YTOU66QI.js");
|
|
7597
7844
|
const { id, delivered } = await deliverEvent2(name, {
|
|
7598
7845
|
type: "webhook",
|
|
7599
7846
|
body,
|
|
@@ -7609,16 +7856,16 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
|
|
|
7609
7856
|
var schedules_default = app15;
|
|
7610
7857
|
|
|
7611
7858
|
// packages/daemon/src/web/api/setup.ts
|
|
7612
|
-
import { mkdirSync as mkdirSync7 } from "fs";
|
|
7859
|
+
import { mkdirSync as mkdirSync7, rmSync as rmSync4, writeFileSync as writeFileSync9 } from "fs";
|
|
7613
7860
|
import { homedir as homedir2 } from "os";
|
|
7614
|
-
import { resolve as
|
|
7861
|
+
import { basename, resolve as resolve14 } from "path";
|
|
7615
7862
|
import { Hono as Hono19 } from "hono";
|
|
7616
7863
|
import { setCookie as setCookie2 } from "hono/cookie";
|
|
7617
7864
|
var DEFAULT_API_URL2 = "https://volute.systems";
|
|
7618
7865
|
var setup = new Hono19();
|
|
7619
7866
|
function writeSetupConfig(systemName, description) {
|
|
7620
|
-
const configHome = process.env.VOLUTE_HOME ??
|
|
7621
|
-
const mindsDir =
|
|
7867
|
+
const configHome = process.env.VOLUTE_HOME ?? resolve14(homedir2(), ".volute");
|
|
7868
|
+
const mindsDir = resolve14(configHome, "minds");
|
|
7622
7869
|
mkdirSync7(configHome, { recursive: true });
|
|
7623
7870
|
mkdirSync7(mindsDir, { recursive: true });
|
|
7624
7871
|
const existingConfig = readGlobalConfig();
|
|
@@ -7652,7 +7899,7 @@ setup.get("/status", async (c) => {
|
|
|
7652
7899
|
let hasAccount = false;
|
|
7653
7900
|
if (hasSystem) {
|
|
7654
7901
|
try {
|
|
7655
|
-
const { listUsersByType: listUsersByType2 } = await import("./auth-
|
|
7902
|
+
const { listUsersByType: listUsersByType2 } = await import("./auth-KRDSM55Q.js");
|
|
7656
7903
|
const brains = await listUsersByType2("human");
|
|
7657
7904
|
hasAccount = brains.length > 0;
|
|
7658
7905
|
} catch (err) {
|
|
@@ -7864,7 +8111,7 @@ setup.post("/account", async (c) => {
|
|
|
7864
8111
|
}
|
|
7865
8112
|
}
|
|
7866
8113
|
try {
|
|
7867
|
-
const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-
|
|
8114
|
+
const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-KRDSM55Q.js");
|
|
7868
8115
|
const user = await createUser2(body.username.trim(), body.password);
|
|
7869
8116
|
if (body.displayName?.trim()) {
|
|
7870
8117
|
await updateUserProfile2(user.id, { display_name: body.displayName.trim() });
|
|
@@ -7949,6 +8196,39 @@ setup.post("/spirit", async (c) => {
|
|
|
7949
8196
|
config2.setup.spiritName = name;
|
|
7950
8197
|
const temperament = body.temperament?.trim();
|
|
7951
8198
|
config2.setup.spiritTemperament = temperament || void 0;
|
|
8199
|
+
const description = body.description?.trim();
|
|
8200
|
+
config2.setup.spiritDescription = description || void 0;
|
|
8201
|
+
const clearOldStash = () => {
|
|
8202
|
+
if (config2.setup?.spiritAvatar) {
|
|
8203
|
+
rmSync4(resolve14(voluteSystemDir(), basename(config2.setup.spiritAvatar)), { force: true });
|
|
8204
|
+
config2.setup.spiritAvatar = void 0;
|
|
8205
|
+
}
|
|
8206
|
+
};
|
|
8207
|
+
if (body.avatar) {
|
|
8208
|
+
const match = /^data:image\/(png|jpeg|webp);base64,([A-Za-z0-9+/=]+)$/.exec(body.avatar);
|
|
8209
|
+
if (!match) {
|
|
8210
|
+
return c.json({ error: "Avatar must be a png, jpeg, or webp image" }, 400);
|
|
8211
|
+
}
|
|
8212
|
+
if (match[2].length > 3 * 1024 * 1024) {
|
|
8213
|
+
return c.json({ error: "Avatar must be under 2MB" }, 400);
|
|
8214
|
+
}
|
|
8215
|
+
let bytes = Buffer.from(match[2], "base64");
|
|
8216
|
+
if (bytes.length > 2 * 1024 * 1024) {
|
|
8217
|
+
return c.json({ error: "Avatar must be under 2MB" }, 400);
|
|
8218
|
+
}
|
|
8219
|
+
let ext = match[1] === "jpeg" ? "jpg" : match[1];
|
|
8220
|
+
const normalized = await normalizeAvatar(bytes);
|
|
8221
|
+
if (normalized) {
|
|
8222
|
+
bytes = normalized.buffer;
|
|
8223
|
+
ext = "webp";
|
|
8224
|
+
}
|
|
8225
|
+
clearOldStash();
|
|
8226
|
+
const filename = `spirit-avatar.${ext}`;
|
|
8227
|
+
writeFileSync9(resolve14(voluteSystemDir(), filename), bytes);
|
|
8228
|
+
config2.setup.spiritAvatar = filename;
|
|
8229
|
+
} else {
|
|
8230
|
+
clearOldStash();
|
|
8231
|
+
}
|
|
7952
8232
|
writeGlobalConfig(config2);
|
|
7953
8233
|
return c.json({ ok: true });
|
|
7954
8234
|
});
|
|
@@ -7957,8 +8237,8 @@ setup.post("/complete", async (c) => {
|
|
|
7957
8237
|
return c.json({ error: "Setup already complete" }, 400);
|
|
7958
8238
|
}
|
|
7959
8239
|
try {
|
|
7960
|
-
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-
|
|
7961
|
-
const { startSpiritFull } = await import("./mind-service-
|
|
8240
|
+
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-I5ECLGRI.js");
|
|
8241
|
+
const { startSpiritFull } = await import("./mind-service-WHFBUKZE.js");
|
|
7962
8242
|
await ensureSpiritProject();
|
|
7963
8243
|
await syncSpiritTemplate();
|
|
7964
8244
|
const warnings = [];
|
|
@@ -7995,7 +8275,7 @@ setup.post("/complete", async (c) => {
|
|
|
7995
8275
|
}
|
|
7996
8276
|
let spiritConversationId;
|
|
7997
8277
|
try {
|
|
7998
|
-
const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-
|
|
8278
|
+
const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-KRDSM55Q.js");
|
|
7999
8279
|
const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-ZL6PHROD.js");
|
|
8000
8280
|
const spiritUser = await getOrCreateMindUser2(spiritName);
|
|
8001
8281
|
const brains = await listUsersByType2("human");
|
|
@@ -8018,15 +8298,13 @@ setup.post("/complete", async (c) => {
|
|
|
8018
8298
|
logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
|
|
8019
8299
|
if (spiritConversationId && spiritStarted) {
|
|
8020
8300
|
try {
|
|
8021
|
-
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-
|
|
8022
|
-
const { listUsersByType: listUsers2 } = await import("./auth-
|
|
8301
|
+
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-TUXM3AAE.js");
|
|
8302
|
+
const { listUsersByType: listUsers2 } = await import("./auth-KRDSM55Q.js");
|
|
8023
8303
|
const admins = await listUsers2("human");
|
|
8024
8304
|
const admin2 = admins.find((u) => u.role === "admin");
|
|
8025
8305
|
if (admin2) {
|
|
8026
8306
|
const adminName = admin2.display_name || admin2.username;
|
|
8027
|
-
const
|
|
8028
|
-
const temperament = cfg.setup?.spiritTemperament;
|
|
8029
|
-
const welcomePrompt = `Setup is complete. You are ${spiritName}, the spirit of ${cfg.name ?? "this system"}` + (cfg.description ? ` \u2014 ${cfg.description}` : "") + `. ${adminName} chose your name just now` + (temperament ? ` and imagined your temperament as: "${temperament}"` : "") + `, and is now in your chat. Greet ${adminName} by name and help them create their first mind. Keep it brief \u2014 a short greeting in your own voice, then ask what kind of being they'd like to bring into the world. Remember: minds are beings with their own experience, not tools \u2014 ask about personality and character, never about tasks or utility.`;
|
|
8307
|
+
const welcomePrompt = `Setup is complete, and ${adminName} is now in your chat. Greet ${adminName} by name and help them create their first mind. Keep it brief \u2014 a short greeting in your own voice, then ask what kind of being they'd like to bring into the world. Remember: minds are beings with their own experience, not tools \u2014 ask about personality and character, never about tasks or utility.`;
|
|
8030
8308
|
await deliverMessage2(spiritName, {
|
|
8031
8309
|
content: [{ type: "text", text: welcomePrompt }],
|
|
8032
8310
|
channel: `@${admin2.username}`,
|
|
@@ -8060,9 +8338,9 @@ setup.post("/complete", async (c) => {
|
|
|
8060
8338
|
var setup_default = setup;
|
|
8061
8339
|
|
|
8062
8340
|
// packages/daemon/src/web/api/skills.ts
|
|
8063
|
-
import { existsSync as existsSync9, mkdtempSync, readdirSync as readdirSync3, rmSync as
|
|
8341
|
+
import { existsSync as existsSync9, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync5 } from "fs";
|
|
8064
8342
|
import { tmpdir } from "os";
|
|
8065
|
-
import { join, resolve as
|
|
8343
|
+
import { join, resolve as resolve15 } from "path";
|
|
8066
8344
|
import AdmZip from "adm-zip";
|
|
8067
8345
|
import { Hono as Hono20 } from "hono";
|
|
8068
8346
|
var app16 = new Hono20().get("/", async (c) => {
|
|
@@ -8138,7 +8416,7 @@ var app16 = new Hono20().get("/", async (c) => {
|
|
|
8138
8416
|
try {
|
|
8139
8417
|
const zip = new AdmZip(buffer);
|
|
8140
8418
|
for (const entry of zip.getEntries()) {
|
|
8141
|
-
const target =
|
|
8419
|
+
const target = resolve15(tmpDir, entry.entryName);
|
|
8142
8420
|
if (!target.startsWith(tmpDir)) {
|
|
8143
8421
|
return c.json({ error: "Invalid zip: paths must not escape archive" }, 400);
|
|
8144
8422
|
}
|
|
@@ -8167,7 +8445,7 @@ var app16 = new Hono20().get("/", async (c) => {
|
|
|
8167
8445
|
}
|
|
8168
8446
|
throw e;
|
|
8169
8447
|
} finally {
|
|
8170
|
-
|
|
8448
|
+
rmSync5(tmpDir, { recursive: true, force: true });
|
|
8171
8449
|
}
|
|
8172
8450
|
}).get("/:id", async (c) => {
|
|
8173
8451
|
const id = c.req.param("id");
|
|
@@ -8512,8 +8790,8 @@ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
8512
8790
|
});
|
|
8513
8791
|
}, 15e3);
|
|
8514
8792
|
cleanups.push(() => clearInterval(keepAlive));
|
|
8515
|
-
await new Promise((
|
|
8516
|
-
stream.onAbort(() =>
|
|
8793
|
+
await new Promise((resolve21) => {
|
|
8794
|
+
stream.onAbort(() => resolve21());
|
|
8517
8795
|
});
|
|
8518
8796
|
} finally {
|
|
8519
8797
|
for (const cleanup of cleanups) {
|
|
@@ -8794,19 +9072,19 @@ var app21 = new Hono25().use("*", authMiddleware).get("/", async (c) => {
|
|
|
8794
9072
|
var feed_default = app21;
|
|
8795
9073
|
|
|
8796
9074
|
// packages/daemon/src/web/api/variants.ts
|
|
8797
|
-
import { existsSync as existsSync11, mkdirSync as mkdirSync9, writeFileSync as
|
|
8798
|
-
import { resolve as
|
|
9075
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync9, writeFileSync as writeFileSync10 } from "fs";
|
|
9076
|
+
import { resolve as resolve18 } from "path";
|
|
8799
9077
|
import { Hono as Hono26 } from "hono";
|
|
8800
9078
|
|
|
8801
9079
|
// packages/daemon/src/lib/mind/farewell.ts
|
|
8802
|
-
import { existsSync as existsSync10, readFileSync as readFileSync10, rmSync as
|
|
8803
|
-
import { resolve as
|
|
9080
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10, rmSync as rmSync6 } from "fs";
|
|
9081
|
+
import { resolve as resolve16 } from "path";
|
|
8804
9082
|
var flog = logger_default.child("farewell");
|
|
8805
9083
|
var FAREWELL_RELPATH = ".mind/farewell.md";
|
|
8806
9084
|
var FAREWELL_MIND_PATH = "../.mind/farewell.md";
|
|
8807
9085
|
var FAREWELL_TIMEOUT_MS = 12e4;
|
|
8808
9086
|
function farewellPath(variantDir) {
|
|
8809
|
-
return
|
|
9087
|
+
return resolve16(variantDir, FAREWELL_RELPATH);
|
|
8810
9088
|
}
|
|
8811
9089
|
function readFarewell(variantDir) {
|
|
8812
9090
|
const path = farewellPath(variantDir);
|
|
@@ -8842,7 +9120,7 @@ async function runFarewellTurn(opts) {
|
|
|
8842
9120
|
const timeoutMs = opts.timeoutMs ?? FAREWELL_TIMEOUT_MS;
|
|
8843
9121
|
if (!running) return readFarewell(variantDir);
|
|
8844
9122
|
try {
|
|
8845
|
-
|
|
9123
|
+
rmSync6(farewellPath(variantDir), { force: true });
|
|
8846
9124
|
} catch (err) {
|
|
8847
9125
|
flog.warn(`failed to clear stale farewell note for ${variantName}`, logger_default.errorData(err));
|
|
8848
9126
|
}
|
|
@@ -8866,8 +9144,8 @@ async function runFarewellTurn(opts) {
|
|
|
8866
9144
|
|
|
8867
9145
|
// packages/daemon/src/lib/mind/spawn-server.ts
|
|
8868
9146
|
import { spawn as spawn4 } from "child_process";
|
|
8869
|
-
import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync11, statSync as
|
|
8870
|
-
import { resolve as
|
|
9147
|
+
import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync11, statSync as statSync4 } from "fs";
|
|
9148
|
+
import { resolve as resolve17 } from "path";
|
|
8871
9149
|
async function spawnServer(cwd, port, options) {
|
|
8872
9150
|
let cmd = process.env.VOLUTE_NODE_PATH ?? process.execPath;
|
|
8873
9151
|
let args = ["--import", "tsx", "src/server.ts", "--port", String(port)];
|
|
@@ -8888,34 +9166,34 @@ function spawnAttached(cmd, args, cwd) {
|
|
|
8888
9166
|
cwd,
|
|
8889
9167
|
stdio: ["ignore", "pipe", "pipe"]
|
|
8890
9168
|
});
|
|
8891
|
-
return new Promise((
|
|
8892
|
-
const timeout = setTimeout(() =>
|
|
9169
|
+
return new Promise((resolve21) => {
|
|
9170
|
+
const timeout = setTimeout(() => resolve21(null), 3e4);
|
|
8893
9171
|
function checkOutput(data) {
|
|
8894
9172
|
const match = data.toString().match(/listening on :(\d+)/);
|
|
8895
9173
|
if (match) {
|
|
8896
9174
|
clearTimeout(timeout);
|
|
8897
|
-
|
|
9175
|
+
resolve21({ child, actualPort: parseInt(match[1], 10) });
|
|
8898
9176
|
}
|
|
8899
9177
|
}
|
|
8900
9178
|
child.stdout?.on("data", checkOutput);
|
|
8901
9179
|
child.stderr?.on("data", checkOutput);
|
|
8902
9180
|
child.on("error", () => {
|
|
8903
9181
|
clearTimeout(timeout);
|
|
8904
|
-
|
|
9182
|
+
resolve21(null);
|
|
8905
9183
|
});
|
|
8906
9184
|
child.on("exit", () => {
|
|
8907
9185
|
clearTimeout(timeout);
|
|
8908
|
-
|
|
9186
|
+
resolve21(null);
|
|
8909
9187
|
});
|
|
8910
9188
|
});
|
|
8911
9189
|
}
|
|
8912
9190
|
function spawnDetached(cmd, args, cwd, logDir) {
|
|
8913
|
-
const logsDir = logDir ??
|
|
9191
|
+
const logsDir = logDir ?? resolve17(cwd, ".mind", "logs");
|
|
8914
9192
|
mkdirSync8(logsDir, { recursive: true });
|
|
8915
|
-
const logPath =
|
|
9193
|
+
const logPath = resolve17(logsDir, "mind.log");
|
|
8916
9194
|
let startOffset = 0;
|
|
8917
9195
|
try {
|
|
8918
|
-
startOffset =
|
|
9196
|
+
startOffset = statSync4(logPath).size;
|
|
8919
9197
|
} catch {
|
|
8920
9198
|
}
|
|
8921
9199
|
const logFd = openSync(logPath, "a");
|
|
@@ -9053,11 +9331,11 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
9053
9331
|
return c.json({ error: `Name already in use: ${variantName}` }, 409);
|
|
9054
9332
|
}
|
|
9055
9333
|
const projectRoot = entry.dir ?? mindDir(mindName);
|
|
9056
|
-
const variantDir =
|
|
9334
|
+
const variantDir = resolve18(projectRoot, ".variants", variantName);
|
|
9057
9335
|
if (existsSync11(variantDir)) {
|
|
9058
9336
|
return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
|
|
9059
9337
|
}
|
|
9060
|
-
mkdirSync9(
|
|
9338
|
+
mkdirSync9(resolve18(projectRoot, ".variants"), { recursive: true });
|
|
9061
9339
|
try {
|
|
9062
9340
|
await gitExec(["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
|
|
9063
9341
|
} catch (e) {
|
|
@@ -9093,7 +9371,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
9093
9371
|
const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
|
|
9094
9372
|
await exec(cmd, args, {
|
|
9095
9373
|
cwd: variantDir,
|
|
9096
|
-
env: { ...process.env, HOME:
|
|
9374
|
+
env: { ...process.env, HOME: resolve18(variantDir, "home") }
|
|
9097
9375
|
});
|
|
9098
9376
|
} else {
|
|
9099
9377
|
await exec("npm", ["install"], { cwd: variantDir });
|
|
@@ -9104,7 +9382,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
9104
9382
|
return c.json({ error: `npm install failed: ${msg}` }, 500);
|
|
9105
9383
|
}
|
|
9106
9384
|
if (body.soul) {
|
|
9107
|
-
|
|
9385
|
+
writeFileSync10(resolve18(variantDir, "home/SOUL.md"), body.soul);
|
|
9108
9386
|
}
|
|
9109
9387
|
const variantPort = body.port ?? await nextPort();
|
|
9110
9388
|
try {
|
|
@@ -9157,7 +9435,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
9157
9435
|
} catch {
|
|
9158
9436
|
}
|
|
9159
9437
|
const projectRoot = parentEntry.dir ?? mindDir(mindName);
|
|
9160
|
-
const failAfterGitWrite = async (message, extra) => {
|
|
9438
|
+
const failAfterGitWrite = async (message, extra, status = 500) => {
|
|
9161
9439
|
try {
|
|
9162
9440
|
await chownMindDir(projectRoot, mindName);
|
|
9163
9441
|
} catch (err) {
|
|
@@ -9173,7 +9451,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
9173
9451
|
500
|
|
9174
9452
|
);
|
|
9175
9453
|
}
|
|
9176
|
-
return c.json({ error: message, ...extra },
|
|
9454
|
+
return c.json({ error: message, ...extra }, status);
|
|
9177
9455
|
};
|
|
9178
9456
|
let farewell;
|
|
9179
9457
|
try {
|
|
@@ -9245,6 +9523,27 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
9245
9523
|
{ conflicts: err.conflicts }
|
|
9246
9524
|
);
|
|
9247
9525
|
}
|
|
9526
|
+
if (existsSync11(variantEntry.dir) && !body.discardUnresolved) {
|
|
9527
|
+
let unresolved;
|
|
9528
|
+
try {
|
|
9529
|
+
unresolved = await findUnresolvedHomeFiles(variantEntry.dir);
|
|
9530
|
+
} catch (err) {
|
|
9531
|
+
return failAfterGitWrite(
|
|
9532
|
+
`Could not verify variant ${variantName} has no unresolved home/ files: ${err instanceof Error ? err.message : String(err)}. Variant left intact \u2014 retry the join.`
|
|
9533
|
+
);
|
|
9534
|
+
}
|
|
9535
|
+
if (unresolved.totalCount > 0) {
|
|
9536
|
+
return failAfterGitWrite(
|
|
9537
|
+
formatUnresolvedHomeFilesMessage(variantName, unresolved),
|
|
9538
|
+
{
|
|
9539
|
+
unresolvedFiles: unresolved.files,
|
|
9540
|
+
unresolvedCount: unresolved.totalCount,
|
|
9541
|
+
unresolvedBytes: unresolved.totalBytes
|
|
9542
|
+
},
|
|
9543
|
+
409
|
|
9544
|
+
);
|
|
9545
|
+
}
|
|
9546
|
+
}
|
|
9248
9547
|
await cleanupVariant(variantName, mindName, projectRoot, variantEntry.dir, { stop: true });
|
|
9249
9548
|
await chownMindDir(projectRoot, mindName);
|
|
9250
9549
|
if (variantName.endsWith("-upgrade") || variantName === "upgrade") {
|
|
@@ -9263,7 +9562,7 @@ var app22 = new Hono26().get("/:name/variants", async (c) => {
|
|
|
9263
9562
|
const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
|
|
9264
9563
|
await exec(cmd, args, {
|
|
9265
9564
|
cwd: projectRoot,
|
|
9266
|
-
env: { ...process.env, HOME:
|
|
9565
|
+
env: { ...process.env, HOME: resolve18(projectRoot, "home") }
|
|
9267
9566
|
});
|
|
9268
9567
|
} else {
|
|
9269
9568
|
await exec("npm", ["install"], { cwd: projectRoot });
|
|
@@ -9720,7 +10019,7 @@ var unifiedChatApp = new Hono28().post(
|
|
|
9720
10019
|
logger_default.error("spirit availability check failed \u2014 proceeding with delivery", logger_default.errorData(err));
|
|
9721
10020
|
}
|
|
9722
10021
|
const isDM = conv.type === "dm";
|
|
9723
|
-
await fanOutToMinds({
|
|
10022
|
+
const { gatedRecipients } = await fanOutToMinds({
|
|
9724
10023
|
conversationId,
|
|
9725
10024
|
contentBlocks,
|
|
9726
10025
|
senderName,
|
|
@@ -9730,7 +10029,15 @@ var unifiedChatApp = new Hono28().post(
|
|
|
9730
10029
|
// Variant-aware targeting: when targetMind is a variant, route to the variant name
|
|
9731
10030
|
targetName: baseName ? (username) => username === baseName ? variantName : username : void 0
|
|
9732
10031
|
});
|
|
9733
|
-
|
|
10032
|
+
const notice = gatedRecipients.length > 0 ? `Note: your message is held for ${gatedRecipients.join(", ")} pending channel approval \u2014 they haven't routed this channel yet. It will be delivered if they accept the channel.` : void 0;
|
|
10033
|
+
return c.json({
|
|
10034
|
+
ok: true,
|
|
10035
|
+
conversationId,
|
|
10036
|
+
outboundId,
|
|
10037
|
+
spirit: spiritStatus,
|
|
10038
|
+
spiritName,
|
|
10039
|
+
...notice ? { notice } : {}
|
|
10040
|
+
});
|
|
9734
10041
|
}
|
|
9735
10042
|
);
|
|
9736
10043
|
var app24 = new Hono28().get("/:name/conversations/:id/events", async (c) => {
|
|
@@ -9750,11 +10057,11 @@ var app24 = new Hono28().get("/:name/conversations/:id/events", async (c) => {
|
|
|
9750
10057
|
if (!stream.aborted) console.error("[chat] SSE ping error:", err);
|
|
9751
10058
|
});
|
|
9752
10059
|
}, 15e3);
|
|
9753
|
-
await new Promise((
|
|
10060
|
+
await new Promise((resolve21) => {
|
|
9754
10061
|
stream.onAbort(() => {
|
|
9755
10062
|
unsubscribe();
|
|
9756
10063
|
clearInterval(keepAlive);
|
|
9757
|
-
|
|
10064
|
+
resolve21();
|
|
9758
10065
|
});
|
|
9759
10066
|
});
|
|
9760
10067
|
});
|
|
@@ -9974,7 +10281,7 @@ var app_default = app26;
|
|
|
9974
10281
|
import { existsSync as existsSync12 } from "fs";
|
|
9975
10282
|
import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
9976
10283
|
import { createServer as createHttpsServer } from "https";
|
|
9977
|
-
import { dirname as dirname3, extname as extname3, resolve as
|
|
10284
|
+
import { dirname as dirname3, extname as extname3, resolve as resolve19 } from "path";
|
|
9978
10285
|
import { serve } from "@hono/node-server";
|
|
9979
10286
|
var MIME_TYPES2 = {
|
|
9980
10287
|
".html": "text/html",
|
|
@@ -9993,7 +10300,7 @@ async function startServer({
|
|
|
9993
10300
|
let assetsDir = "";
|
|
9994
10301
|
let searchDir = dirname3(new URL(import.meta.url).pathname);
|
|
9995
10302
|
for (let i = 0; i < 5; i++) {
|
|
9996
|
-
const candidate =
|
|
10303
|
+
const candidate = resolve19(searchDir, "dist", "web-assets");
|
|
9997
10304
|
if (existsSync12(candidate)) {
|
|
9998
10305
|
assetsDir = candidate;
|
|
9999
10306
|
break;
|
|
@@ -10004,20 +10311,20 @@ async function startServer({
|
|
|
10004
10311
|
app_default.get("*", async (c) => {
|
|
10005
10312
|
const urlPath = new URL(c.req.url).pathname;
|
|
10006
10313
|
if (urlPath.startsWith("/api/") || urlPath.startsWith("/ext/")) return c.notFound();
|
|
10007
|
-
const filePath =
|
|
10314
|
+
const filePath = resolve19(assetsDir, urlPath.slice(1));
|
|
10008
10315
|
if (!filePath.startsWith(assetsDir)) return c.text("Forbidden", 403);
|
|
10009
10316
|
const s = await stat2(filePath).catch(() => null);
|
|
10010
10317
|
if (s?.isFile()) {
|
|
10011
10318
|
const ext = extname3(filePath);
|
|
10012
10319
|
const mime = MIME_TYPES2[ext] || "application/octet-stream";
|
|
10013
10320
|
const body = await readFile2(filePath);
|
|
10014
|
-
const
|
|
10015
|
-
const nameWithoutExt =
|
|
10321
|
+
const basename2 = filePath.slice(filePath.lastIndexOf("/") + 1);
|
|
10322
|
+
const nameWithoutExt = basename2.slice(0, basename2.lastIndexOf("."));
|
|
10016
10323
|
const isHashed = /[-.][\da-f]{8,}$/.test(nameWithoutExt);
|
|
10017
10324
|
const cacheControl = isHashed ? "public, max-age=31536000, immutable" : "no-cache";
|
|
10018
10325
|
return c.body(body, 200, { "Content-Type": mime, "Cache-Control": cacheControl });
|
|
10019
10326
|
}
|
|
10020
|
-
const indexPath =
|
|
10327
|
+
const indexPath = resolve19(assetsDir, "index.html");
|
|
10021
10328
|
const indexStat = await stat2(indexPath).catch(() => null);
|
|
10022
10329
|
if (indexStat?.isFile()) {
|
|
10023
10330
|
const body = await readFile2(indexPath, "utf-8");
|
|
@@ -10034,10 +10341,10 @@ async function startServer({
|
|
|
10034
10341
|
createServer: createHttpsServer,
|
|
10035
10342
|
serverOptions: { key: tls.key, cert: tls.cert }
|
|
10036
10343
|
});
|
|
10037
|
-
await new Promise((
|
|
10344
|
+
await new Promise((resolve21, reject) => {
|
|
10038
10345
|
server2.on("listening", () => {
|
|
10039
10346
|
logger_default.info("Volute UI running (https)", { hostname, port });
|
|
10040
|
-
|
|
10347
|
+
resolve21();
|
|
10041
10348
|
});
|
|
10042
10349
|
server2.on("error", (err) => {
|
|
10043
10350
|
reject(err);
|
|
@@ -10045,13 +10352,13 @@ async function startServer({
|
|
|
10045
10352
|
});
|
|
10046
10353
|
const internalPort = port + 1;
|
|
10047
10354
|
const internalServer = serve({ fetch: app_default.fetch, port: internalPort, hostname: "127.0.0.1" });
|
|
10048
|
-
await new Promise((
|
|
10355
|
+
await new Promise((resolve21, reject) => {
|
|
10049
10356
|
internalServer.on("listening", () => {
|
|
10050
10357
|
logger_default.info("Volute API running (http, internal)", {
|
|
10051
10358
|
hostname: "127.0.0.1",
|
|
10052
10359
|
port: internalPort
|
|
10053
10360
|
});
|
|
10054
|
-
|
|
10361
|
+
resolve21();
|
|
10055
10362
|
});
|
|
10056
10363
|
internalServer.on("error", (err) => {
|
|
10057
10364
|
reject(err);
|
|
@@ -10060,10 +10367,10 @@ async function startServer({
|
|
|
10060
10367
|
return { server: server2, internalPort };
|
|
10061
10368
|
}
|
|
10062
10369
|
const server = serve({ fetch: app_default.fetch, port, hostname });
|
|
10063
|
-
await new Promise((
|
|
10370
|
+
await new Promise((resolve21, reject) => {
|
|
10064
10371
|
server.on("listening", () => {
|
|
10065
10372
|
logger_default.info("Volute API running (http)", { hostname, port });
|
|
10066
|
-
|
|
10373
|
+
resolve21();
|
|
10067
10374
|
});
|
|
10068
10375
|
server.on("error", (err) => {
|
|
10069
10376
|
reject(err);
|
|
@@ -10074,18 +10381,18 @@ async function startServer({
|
|
|
10074
10381
|
|
|
10075
10382
|
// packages/daemon/src/daemon.ts
|
|
10076
10383
|
if (!process.env.VOLUTE_HOME) {
|
|
10077
|
-
process.env.VOLUTE_HOME =
|
|
10384
|
+
process.env.VOLUTE_HOME = resolve20(homedir3(), ".volute");
|
|
10078
10385
|
}
|
|
10079
10386
|
if (process.env.VOLUTE_TIMEZONE && !process.env.TZ) {
|
|
10080
10387
|
process.env.TZ = process.env.VOLUTE_TIMEZONE;
|
|
10081
10388
|
}
|
|
10082
10389
|
function writeDaemonConfig(systemDir, config2, token) {
|
|
10083
|
-
const configPath =
|
|
10084
|
-
|
|
10390
|
+
const configPath = resolve20(systemDir, "daemon.json");
|
|
10391
|
+
writeFileSync11(configPath, `${JSON.stringify(config2, null, 2)}
|
|
10085
10392
|
`, { mode: 420 });
|
|
10086
10393
|
chmodSync(configPath, 420);
|
|
10087
|
-
const tokenPath =
|
|
10088
|
-
|
|
10394
|
+
const tokenPath = resolve20(systemDir, "daemon-token");
|
|
10395
|
+
writeFileSync11(tokenPath, `${token}
|
|
10089
10396
|
`, { mode: 384 });
|
|
10090
10397
|
chmodSync(tokenPath, 384);
|
|
10091
10398
|
}
|
|
@@ -10095,7 +10402,7 @@ async function startDaemon(opts) {
|
|
|
10095
10402
|
const home = voluteHome();
|
|
10096
10403
|
const systemDir = voluteSystemDir();
|
|
10097
10404
|
if (!opts.foreground) {
|
|
10098
|
-
const rotatingLog = new RotatingLog(
|
|
10405
|
+
const rotatingLog = new RotatingLog(resolve20(systemDir, "daemon.log"));
|
|
10099
10406
|
logger_default.setOutput((line) => rotatingLog.write(`${line}
|
|
10100
10407
|
`));
|
|
10101
10408
|
const write = (...args) => rotatingLog.write(`${format(...args)}
|
|
@@ -10105,9 +10412,9 @@ async function startDaemon(opts) {
|
|
|
10105
10412
|
console.warn = write;
|
|
10106
10413
|
console.info = write;
|
|
10107
10414
|
}
|
|
10108
|
-
const DAEMON_PID_PATH =
|
|
10109
|
-
const DAEMON_JSON_PATH =
|
|
10110
|
-
const DAEMON_TOKEN_PATH =
|
|
10415
|
+
const DAEMON_PID_PATH = resolve20(systemDir, "daemon.pid");
|
|
10416
|
+
const DAEMON_JSON_PATH = resolve20(systemDir, "daemon.json");
|
|
10417
|
+
const DAEMON_TOKEN_PATH = resolve20(systemDir, "daemon-token");
|
|
10111
10418
|
mkdirSync10(home, { recursive: true });
|
|
10112
10419
|
ensureSystemDir();
|
|
10113
10420
|
const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-WZSXHHRJ.js");
|
|
@@ -10168,7 +10475,7 @@ async function startDaemon(opts) {
|
|
|
10168
10475
|
logger_default.warn("failed to backfill minds into #system", logger_default.errorData(err));
|
|
10169
10476
|
}
|
|
10170
10477
|
try {
|
|
10171
|
-
const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-
|
|
10478
|
+
const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-KRDSM55Q.js");
|
|
10172
10479
|
await getOrCreateSystemUser2();
|
|
10173
10480
|
} catch (err) {
|
|
10174
10481
|
logger_default.warn(
|
|
@@ -10209,7 +10516,7 @@ async function startDaemon(opts) {
|
|
|
10209
10516
|
process.env.VOLUTE_DAEMON_TOKEN = token;
|
|
10210
10517
|
process.env.VOLUTE_DAEMON_PORT = String(daemonPort);
|
|
10211
10518
|
process.env.VOLUTE_DAEMON_HOSTNAME = hostname;
|
|
10212
|
-
|
|
10519
|
+
writeFileSync11(DAEMON_PID_PATH, myPid, { mode: 420 });
|
|
10213
10520
|
const daemonConfig = { port, hostname };
|
|
10214
10521
|
if (internalPort) daemonConfig.internalPort = internalPort;
|
|
10215
10522
|
if (tls) daemonConfig.tls = true;
|
|
@@ -10236,7 +10543,7 @@ async function startDaemon(opts) {
|
|
|
10236
10543
|
const orphanedTurns = await completeOrphanedTurns();
|
|
10237
10544
|
summarizeOrphanedTurns(orphanedTurns);
|
|
10238
10545
|
try {
|
|
10239
|
-
const { reconcileVariants } = await import("./variant-cleanup-
|
|
10546
|
+
const { reconcileVariants } = await import("./variant-cleanup-CZDLIY3F.js");
|
|
10240
10547
|
await reconcileVariants();
|
|
10241
10548
|
} catch (err) {
|
|
10242
10549
|
logger_default.error("failed to reconcile variants", logger_default.errorData(err));
|
|
@@ -10272,8 +10579,8 @@ async function startDaemon(opts) {
|
|
|
10272
10579
|
try {
|
|
10273
10580
|
const { isSetupComplete: isSetupComplete2, getSpiritName: getSpiritName2 } = await import("./setup-WZSXHHRJ.js");
|
|
10274
10581
|
if (isSetupComplete2()) {
|
|
10275
|
-
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-
|
|
10276
|
-
const { startSpiritFull } = await import("./mind-service-
|
|
10582
|
+
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-I5ECLGRI.js");
|
|
10583
|
+
const { startSpiritFull } = await import("./mind-service-WHFBUKZE.js");
|
|
10277
10584
|
await ensureSpiritProject();
|
|
10278
10585
|
const spiritName = getSpiritName2();
|
|
10279
10586
|
const spiritEntry = await findMind(spiritName);
|
|
@@ -10292,7 +10599,7 @@ async function startDaemon(opts) {
|
|
|
10292
10599
|
bridgeManager.startBridges(daemonPort).catch((err) => {
|
|
10293
10600
|
logger_default.warn("failed to start bridges", logger_default.errorData(err));
|
|
10294
10601
|
});
|
|
10295
|
-
import("./cloud-sync-
|
|
10602
|
+
import("./cloud-sync-MJVJ33LD.js").then(
|
|
10296
10603
|
({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
|
|
10297
10604
|
logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
|
|
10298
10605
|
})
|
|
@@ -10300,7 +10607,7 @@ async function startDaemon(opts) {
|
|
|
10300
10607
|
logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
|
|
10301
10608
|
});
|
|
10302
10609
|
try {
|
|
10303
|
-
const { backfillTemplateHashes, notifyVersionUpdate, warnStaleTemplates } = await import("./version-notify-
|
|
10610
|
+
const { backfillTemplateHashes, notifyVersionUpdate, warnStaleTemplates } = await import("./version-notify-SJVAQLZI.js");
|
|
10304
10611
|
backfillTemplateHashes().then(() => warnStaleTemplates()).catch((err) => {
|
|
10305
10612
|
logger_default.warn("failed to check template staleness", logger_default.errorData(err));
|
|
10306
10613
|
});
|