volute 0.45.2 → 0.46.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/{activity-events-BUR2RYTJ.js → activity-events-NF6VWTJQ.js} +1 -1
- package/dist/{auth-FLAEXXZK.js → auth-CCFAU4GQ.js} +2 -2
- package/dist/{chat-76MRWWQX.js → chat-ZIQQS25U.js} +1 -1
- package/dist/chunk-4YU7HF4L.js +237 -0
- package/dist/{chunk-OP4JG2BS.js → chunk-5ZQZDDIK.js} +6 -2
- package/dist/{chunk-FR6S2KNN.js → chunk-BJZ4PV7R.js} +1 -1
- package/dist/{chunk-NYC5U6K7.js → chunk-E3DA4HYV.js} +4 -4
- package/dist/{chunk-YT7PEESJ.js → chunk-JO5IO4FH.js} +245 -124
- package/dist/{chunk-7ZUB63PS.js → chunk-KGIXX4NM.js} +1 -1
- package/dist/{chunk-EJVPTVTI.js → chunk-KQSFTPIM.js} +1 -1
- package/dist/{chunk-T6HYVHPG.js → chunk-OV7ZQ2GH.js} +1 -1
- package/dist/{clock-K43IMROX.js → chunk-PJ45FQPT.js} +21 -16
- package/dist/{chunk-UYLGEDVD.js → chunk-YXSUMLTC.js} +44 -267
- package/dist/{chunk-7C44R625.js → chunk-ZNMCCHCP.js} +47 -1
- package/dist/cli.js +11 -11
- package/dist/clock-RHS2JEDL.js +16 -0
- package/dist/{cloud-sync-3OJOPZRT.js → cloud-sync-46RS4SDA.js} +9 -8
- package/dist/{conversations-JZTD6KLR.js → conversations-CJOOCGNW.js} +2 -2
- package/dist/{daemon-restart-TIHKVVVA.js → daemon-restart-VFEI57UO.js} +1 -1
- package/dist/daemon.js +249 -170
- package/dist/{delete-BFMIGAIB.js → delete-HPLC7ETU.js} +1 -1
- package/dist/{delivery-manager-VDVKVA46.js → delivery-manager-JINZRG2W.js} +9 -8
- package/dist/{echo-text-6JJOPUD4.js → echo-text-VQPB5L2X.js} +10 -9
- package/dist/{export-GGIVO76O.js → export-LY723I3Z.js} +3 -3
- package/dist/{extensions-JONTW2HF.js → extensions-ZELQLTSR.js} +9 -8
- package/dist/{message-delivery-3C65ZI6L.js → message-delivery-R7JSQGYX.js} +11 -8
- package/dist/{mind-DWB6XYCH.js → mind-LHKS55DI.js} +9 -9
- package/dist/{mind-activity-tracker-POBLU3LC.js → mind-activity-tracker-3QRNJVR4.js} +2 -2
- package/dist/{mind-service-3F4I46CI.js → mind-service-NB2JMWQZ.js} +11 -8
- package/dist/mind-sleep-74TGOQBC.js +83 -0
- package/dist/{package-A7KXT67Q.js → package-DOD6CPO6.js} +2 -1
- package/dist/{scheduler-P6DXLLXI.js → scheduler-WVKII3LA.js} +9 -8
- package/dist/{seed-HCP5NYKH.js → seed-CNYIHPOH.js} +1 -1
- package/dist/{seed-cmd-ILOGNIFB.js → seed-cmd-AVNUNLZV.js} +2 -2
- package/dist/{seed-create-OURS2LS4.js → seed-create-ANXGYSCC.js} +3 -3
- package/dist/{seed-sprout-ELDFDAN6.js → seed-sprout-NIUFTX32.js} +5 -3
- package/dist/{send-AAIOPJAV.js → send-SPE6DVNJ.js} +6 -0
- package/dist/{setup-DQM5VPY4.js → setup-QT2A25SY.js} +1 -1
- package/dist/skills/orientation/SKILL.md +1 -1
- package/dist/skills/volute-mind/SKILL.md +8 -8
- package/dist/skills/volute-mind/references/sleep.md +8 -1
- package/dist/{skills-BKINFL7W.js → skills-AKCFA7BN.js} +1 -1
- package/dist/{sleep-manager-HCUH4PM4.js → sleep-manager-G6TXSDP7.js} +9 -8
- package/dist/{spirit-DBTIW7P4.js → spirit-RPBLLUQ5.js} +2 -2
- package/dist/{sprout-W3KC7UGK.js → sprout-SMX42ZBP.js} +1 -1
- package/dist/{system-chat-XOALE2RD.js → system-chat-NDAW6YP7.js} +9 -8
- package/dist/{turn-tracker-2W6Q2NSR.js → turn-tracker-FOIBNUUA.js} +2 -1
- package/dist/{up-FTK7CTO3.js → up-PS6PFS75.js} +1 -1
- package/dist/{version-notify-3XMAIQTE.js → version-notify-C5CH7CVP.js} +15 -10
- package/dist/web-assets/assets/{index-Bc2m8gRC.js → index-CU6XWICM.js} +3 -3
- package/dist/web-assets/index.html +1 -1
- package/package.json +2 -1
- package/templates/_base/home/VOLUTE.md +6 -2
- package/templates/_base/src/lib/context-breakdown.ts +57 -67
- package/templates/_base/src/lib/volute-server.ts +2 -2
- package/templates/claude/src/agent.ts +78 -42
- package/templates/claude/src/lib/session-reaper.ts +31 -0
- package/templates/codex/src/agent.ts +62 -40
- package/templates/pi/src/agent.ts +55 -38
- package/dist/mind-sleep-YMBR3I35.js +0 -47
- /package/dist/{chunk-QNQMSWZE.js → chunk-K6T43YBD.js} +0 -0
package/dist/daemon.js
CHANGED
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
fixModelEscapes,
|
|
43
43
|
getPlatformDriver,
|
|
44
44
|
routeOutboundBridge
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-E3DA4HYV.js";
|
|
46
46
|
import {
|
|
47
47
|
getBridgeConfig,
|
|
48
48
|
readBridgesConfig,
|
|
@@ -113,7 +113,7 @@ import {
|
|
|
113
113
|
syncProviderToMinds,
|
|
114
114
|
uninstallNpmExtension,
|
|
115
115
|
writeSystemsConfig
|
|
116
|
-
} from "./chunk-
|
|
116
|
+
} from "./chunk-JO5IO4FH.js";
|
|
117
117
|
import {
|
|
118
118
|
loadMergedEnv,
|
|
119
119
|
mindEnvPath,
|
|
@@ -121,7 +121,7 @@ import {
|
|
|
121
121
|
sharedEnvPath,
|
|
122
122
|
writeEnv
|
|
123
123
|
} from "./chunk-ZDZISKCL.js";
|
|
124
|
-
import "./chunk-
|
|
124
|
+
import "./chunk-BJZ4PV7R.js";
|
|
125
125
|
import {
|
|
126
126
|
applyInitFiles,
|
|
127
127
|
applyTemplateHomeFiles,
|
|
@@ -150,7 +150,7 @@ import {
|
|
|
150
150
|
syncBuiltinSkills,
|
|
151
151
|
uninstallSkill,
|
|
152
152
|
updateSkill
|
|
153
|
-
} from "./chunk-
|
|
153
|
+
} from "./chunk-KGIXX4NM.js";
|
|
154
154
|
import {
|
|
155
155
|
addMessage,
|
|
156
156
|
createChannel,
|
|
@@ -178,11 +178,11 @@ import {
|
|
|
178
178
|
listConversationsForUser,
|
|
179
179
|
listConversationsWithParticipants,
|
|
180
180
|
markConversationRead,
|
|
181
|
-
publish,
|
|
181
|
+
publish as publish2,
|
|
182
182
|
setConversationPrivate,
|
|
183
183
|
subscribe as subscribe2,
|
|
184
184
|
updateChannelSettings
|
|
185
|
-
} from "./chunk-
|
|
185
|
+
} from "./chunk-5ZQZDDIK.js";
|
|
186
186
|
import {
|
|
187
187
|
readVoluteConfig,
|
|
188
188
|
writeVoluteConfig
|
|
@@ -195,33 +195,36 @@ import {
|
|
|
195
195
|
getActiveMinds,
|
|
196
196
|
onMindEvent,
|
|
197
197
|
stopAll
|
|
198
|
-
} from "./chunk-
|
|
198
|
+
} from "./chunk-KQSFTPIM.js";
|
|
199
199
|
import {
|
|
200
200
|
extractTextContent
|
|
201
201
|
} from "./chunk-4JOQZPCE.js";
|
|
202
202
|
import {
|
|
203
|
-
PROMPT_DEFAULTS,
|
|
204
|
-
PROMPT_KEYS,
|
|
205
203
|
assignSession,
|
|
206
204
|
completeOrphanedTurns,
|
|
207
205
|
completeTurn,
|
|
208
206
|
createTurn,
|
|
209
207
|
getActiveTurnId,
|
|
210
|
-
getMindPromptDefaults,
|
|
211
|
-
getPrompt,
|
|
212
|
-
getPromptIfCustom,
|
|
213
208
|
getToolUseEventId,
|
|
214
209
|
initSummarizer,
|
|
215
210
|
markErrored,
|
|
216
|
-
publish as
|
|
211
|
+
publish as publish3,
|
|
217
212
|
subscribe as subscribe3,
|
|
218
213
|
subscribeAll,
|
|
219
|
-
substitute,
|
|
220
214
|
summarizeOrphanedTurns,
|
|
221
215
|
summarizeTurn,
|
|
222
216
|
takeErrored,
|
|
223
217
|
trackToolUse
|
|
224
|
-
} from "./chunk-
|
|
218
|
+
} from "./chunk-YXSUMLTC.js";
|
|
219
|
+
import {
|
|
220
|
+
ORIENTATION_MARKER,
|
|
221
|
+
PROMPT_DEFAULTS,
|
|
222
|
+
PROMPT_KEYS,
|
|
223
|
+
getMindPromptDefaults,
|
|
224
|
+
getPrompt,
|
|
225
|
+
getPromptIfCustom,
|
|
226
|
+
substitute
|
|
227
|
+
} from "./chunk-4YU7HF4L.js";
|
|
225
228
|
import "./chunk-TQJ6ZYVY.js";
|
|
226
229
|
import {
|
|
227
230
|
chownMindDir,
|
|
@@ -279,11 +282,14 @@ import {
|
|
|
279
282
|
syncMindProfile,
|
|
280
283
|
updateUserProfile,
|
|
281
284
|
verifyUser
|
|
282
|
-
} from "./chunk-
|
|
285
|
+
} from "./chunk-OV7ZQ2GH.js";
|
|
283
286
|
import {
|
|
284
287
|
broadcast,
|
|
288
|
+
getEventsSince,
|
|
289
|
+
nextEventId,
|
|
290
|
+
publish,
|
|
285
291
|
subscribe
|
|
286
|
-
} from "./chunk-
|
|
292
|
+
} from "./chunk-ZNMCCHCP.js";
|
|
287
293
|
import {
|
|
288
294
|
logBuffer,
|
|
289
295
|
logger_default
|
|
@@ -292,6 +298,7 @@ import {
|
|
|
292
298
|
buildVoluteSlug
|
|
293
299
|
} from "./chunk-LGNUFVMR.js";
|
|
294
300
|
import {
|
|
301
|
+
SPIRIT_NAME,
|
|
295
302
|
addMind,
|
|
296
303
|
addVariant,
|
|
297
304
|
daemonLoopback,
|
|
@@ -341,9 +348,12 @@ import { format } from "util";
|
|
|
341
348
|
import { CronExpressionParser } from "cron-parser";
|
|
342
349
|
var blog = logger_default.child("backup");
|
|
343
350
|
var DEFAULT_BACKUP_SCHEDULE = "0 3 * * *";
|
|
351
|
+
var NOTIFY_EVERY_N_FAILURES = 5;
|
|
344
352
|
var BackupManager = class {
|
|
345
353
|
interval = null;
|
|
346
|
-
|
|
354
|
+
/** Epoch-ms of the most recent scheduled fire this process has serviced. */
|
|
355
|
+
lastServicedFireMs = -1;
|
|
356
|
+
consecutiveFailures = 0;
|
|
347
357
|
warnedUnconfigured = false;
|
|
348
358
|
start() {
|
|
349
359
|
this.interval = setInterval(() => {
|
|
@@ -355,9 +365,16 @@ var BackupManager = class {
|
|
|
355
365
|
this.interval = null;
|
|
356
366
|
}
|
|
357
367
|
/**
|
|
358
|
-
* True at most once per scheduled fire
|
|
359
|
-
*
|
|
360
|
-
*
|
|
368
|
+
* True at most once per scheduled fire (records the fire it consumed), and
|
|
369
|
+
* only when backups are enabled and runnable. Not a pure predicate — a second
|
|
370
|
+
* call for the same fire returns false.
|
|
371
|
+
*
|
|
372
|
+
* Catch-up: if the machine was asleep or the daemon was down across a
|
|
373
|
+
* scheduled fire (a laptop at 3am), the fire is picked up on the next tick so
|
|
374
|
+
* long as it falls after the last successful run (`lastRun` in
|
|
375
|
+
* backup-state.json). Without an anchor (no successful backup yet) only the
|
|
376
|
+
* exact on-time minute fires, so freshly enabling backups doesn't trigger an
|
|
377
|
+
* immediate run.
|
|
361
378
|
*/
|
|
362
379
|
isDue(now = /* @__PURE__ */ new Date()) {
|
|
363
380
|
const config2 = readGlobalConfig().backup;
|
|
@@ -372,27 +389,81 @@ var BackupManager = class {
|
|
|
372
389
|
this.warnedUnconfigured = false;
|
|
373
390
|
const cron = config2.schedule || DEFAULT_BACKUP_SCHEDULE;
|
|
374
391
|
const epochMinute = Math.floor(now.getTime() / 6e4);
|
|
375
|
-
|
|
376
|
-
let prevMinute;
|
|
392
|
+
let prevFireMs;
|
|
377
393
|
try {
|
|
378
394
|
const interval = CronExpressionParser.parse(cron, { currentDate: now });
|
|
379
|
-
|
|
395
|
+
prevFireMs = interval.prev().toDate().getTime();
|
|
380
396
|
} catch (err) {
|
|
381
397
|
blog.warn(`invalid backup cron "${cron}"`, logger_default.errorData(err));
|
|
382
398
|
return false;
|
|
383
399
|
}
|
|
384
|
-
if (
|
|
385
|
-
|
|
400
|
+
if (prevFireMs <= this.lastServicedFireMs) return false;
|
|
401
|
+
const lastRunMs = this.readLastRunMs();
|
|
402
|
+
if (lastRunMs !== null && prevFireMs <= lastRunMs) return false;
|
|
403
|
+
const onTime = Math.floor(prevFireMs / 6e4) === epochMinute;
|
|
404
|
+
if (!onTime) {
|
|
405
|
+
if (lastRunMs === null) return false;
|
|
406
|
+
blog.warn(
|
|
407
|
+
`missed scheduled backup fire at ${new Date(prevFireMs).toISOString()}; running catch-up`
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
this.lastServicedFireMs = prevFireMs;
|
|
386
411
|
return true;
|
|
387
412
|
}
|
|
413
|
+
readLastRunMs() {
|
|
414
|
+
const lastRun = readBackupState().lastRun;
|
|
415
|
+
if (!lastRun) return null;
|
|
416
|
+
const ms = Date.parse(lastRun);
|
|
417
|
+
return Number.isNaN(ms) ? null : ms;
|
|
418
|
+
}
|
|
388
419
|
async tick() {
|
|
389
420
|
if (!this.isDue()) return;
|
|
390
421
|
blog.info("scheduled backup starting");
|
|
391
422
|
try {
|
|
392
423
|
await runBackup();
|
|
424
|
+
this.recordSuccess();
|
|
393
425
|
} catch (err) {
|
|
394
426
|
blog.error("scheduled backup failed", logger_default.errorData(err));
|
|
427
|
+
await this.recordFailure(err);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
recordSuccess() {
|
|
431
|
+
this.consecutiveFailures = 0;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Track consecutive scheduled-backup failures and actively notify the operator
|
|
435
|
+
* on the 1st and then every Nth. Returns whether it notified (for tests).
|
|
436
|
+
* Notification failures are swallowed — surfacing a backup failure must never
|
|
437
|
+
* take the daemon's backup loop down with it.
|
|
438
|
+
*/
|
|
439
|
+
async recordFailure(err) {
|
|
440
|
+
this.consecutiveFailures += 1;
|
|
441
|
+
const n = this.consecutiveFailures;
|
|
442
|
+
if (n !== 1 && n % NOTIFY_EVERY_N_FAILURES !== 0) return false;
|
|
443
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
444
|
+
const summary = n === 1 ? `Scheduled backup failed: ${reason}` : `Scheduled backup has failed ${n} times in a row: ${reason}`;
|
|
445
|
+
try {
|
|
446
|
+
await publish({
|
|
447
|
+
type: "backup_failed",
|
|
448
|
+
mind: SPIRIT_NAME,
|
|
449
|
+
summary,
|
|
450
|
+
metadata: { consecutiveFailures: n, error: reason }
|
|
451
|
+
});
|
|
452
|
+
} catch (pubErr) {
|
|
453
|
+
blog.error("failed to publish backup-failure activity", logger_default.errorData(pubErr));
|
|
454
|
+
}
|
|
455
|
+
try {
|
|
456
|
+
const { sendSystemMessage } = await import("./system-chat-NDAW6YP7.js");
|
|
457
|
+
await sendSystemMessage(
|
|
458
|
+
SPIRIT_NAME,
|
|
459
|
+
`${summary}
|
|
460
|
+
|
|
461
|
+
Backups are not being saved. Check \`volute backup status\` and the Backups settings tab.`
|
|
462
|
+
);
|
|
463
|
+
} catch (msgErr) {
|
|
464
|
+
blog.error("failed to send backup-failure system message", logger_default.errorData(msgErr));
|
|
395
465
|
}
|
|
466
|
+
return true;
|
|
396
467
|
}
|
|
397
468
|
};
|
|
398
469
|
var backupManager = null;
|
|
@@ -728,6 +799,26 @@ async function cleanExpiredLogs() {
|
|
|
728
799
|
await db.delete(mindHistory).where(and(eq(mindHistory.type, "log"), lt(mindHistory.created_at, cutoff)));
|
|
729
800
|
}
|
|
730
801
|
|
|
802
|
+
// packages/daemon/src/lib/daemon/maintenance.ts
|
|
803
|
+
var MAINTENANCE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
804
|
+
async function runMaintenance() {
|
|
805
|
+
try {
|
|
806
|
+
await cleanExpiredSessions();
|
|
807
|
+
} catch (err) {
|
|
808
|
+
logger_default.warn("maintenance: failed to clean expired sessions", logger_default.errorData(err));
|
|
809
|
+
}
|
|
810
|
+
try {
|
|
811
|
+
await cleanExpiredLogs();
|
|
812
|
+
} catch (err) {
|
|
813
|
+
logger_default.warn("maintenance: failed to clean expired logs", logger_default.errorData(err));
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
function startMaintenanceInterval(intervalMs = MAINTENANCE_INTERVAL_MS, task = runMaintenance) {
|
|
817
|
+
const handle = setInterval(() => void task(), intervalMs);
|
|
818
|
+
handle.unref?.();
|
|
819
|
+
return handle;
|
|
820
|
+
}
|
|
821
|
+
|
|
731
822
|
// packages/daemon/src/web/api/system.ts
|
|
732
823
|
import { getOAuthProvider, getOAuthProviders } from "@earendil-works/pi-ai/oauth";
|
|
733
824
|
import { getBuiltinProviders } from "@earendil-works/pi-ai/providers/all";
|
|
@@ -1696,17 +1787,17 @@ var authenticated = new Hono3().use(authMiddleware).post("/change-password", zVa
|
|
|
1696
1787
|
if (!AVATAR_MIME[ext]) {
|
|
1697
1788
|
return c.json({ error: "Invalid file type (png, jpg, gif, webp only)" }, 400);
|
|
1698
1789
|
}
|
|
1699
|
-
let
|
|
1790
|
+
let buffer = Buffer.from(await file.arrayBuffer());
|
|
1700
1791
|
let finalExt = ext;
|
|
1701
|
-
const normalized = await normalizeAvatar(
|
|
1792
|
+
const normalized = await normalizeAvatar(buffer);
|
|
1702
1793
|
if (normalized) {
|
|
1703
|
-
|
|
1794
|
+
buffer = normalized.buffer;
|
|
1704
1795
|
finalExt = normalized.ext;
|
|
1705
1796
|
}
|
|
1706
1797
|
const dir = avatarsDir();
|
|
1707
1798
|
mkdirSync2(dir, { recursive: true });
|
|
1708
1799
|
const filename = `avatar-${user.id}${finalExt}`;
|
|
1709
|
-
writeFileSync2(resolve3(dir, filename),
|
|
1800
|
+
writeFileSync2(resolve3(dir, filename), buffer);
|
|
1710
1801
|
if (user.avatar && user.avatar !== filename) {
|
|
1711
1802
|
const oldPath = resolve3(dir, user.avatar);
|
|
1712
1803
|
rmSync(oldPath, { force: true });
|
|
@@ -2168,7 +2259,7 @@ var app4 = new Hono5().post("/:platform/inbound", zValidator4("json", inboundSch
|
|
|
2168
2259
|
}
|
|
2169
2260
|
const participants = await getParticipants(channel.id);
|
|
2170
2261
|
if (!participants.some((p) => p.userId === puppet.id)) {
|
|
2171
|
-
const { addParticipant } = await import("./conversations-
|
|
2262
|
+
const { addParticipant } = await import("./conversations-CJOOCGNW.js");
|
|
2172
2263
|
await addParticipant(channel.id, puppet.id);
|
|
2173
2264
|
}
|
|
2174
2265
|
const contentBlocks = body.content;
|
|
@@ -2452,7 +2543,7 @@ async function notifyMind(mindName, message) {
|
|
|
2452
2543
|
const entry = await findMind(mindName);
|
|
2453
2544
|
if (!entry) return;
|
|
2454
2545
|
try {
|
|
2455
|
-
const { sendSystemMessage } = await import("./system-chat-
|
|
2546
|
+
const { sendSystemMessage } = await import("./system-chat-NDAW6YP7.js");
|
|
2456
2547
|
await sendSystemMessage(mindName, message);
|
|
2457
2548
|
} catch (err) {
|
|
2458
2549
|
logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
|
|
@@ -2621,11 +2712,11 @@ var app9 = new Hono11().post("/:name/avatar", requireSelf(), async (c) => {
|
|
|
2621
2712
|
if (!AVATAR_MIME2[ext]) {
|
|
2622
2713
|
return c.json({ error: "Invalid file type (png, jpg, gif, webp only)" }, 400);
|
|
2623
2714
|
}
|
|
2624
|
-
let
|
|
2715
|
+
let buffer = Buffer.from(await file.arrayBuffer());
|
|
2625
2716
|
let finalExt = ext;
|
|
2626
|
-
const normalized = await normalizeAvatar(
|
|
2717
|
+
const normalized = await normalizeAvatar(buffer);
|
|
2627
2718
|
if (normalized) {
|
|
2628
|
-
|
|
2719
|
+
buffer = normalized.buffer;
|
|
2629
2720
|
finalExt = normalized.ext;
|
|
2630
2721
|
}
|
|
2631
2722
|
const dir = entry.dir ?? mindDir(name);
|
|
@@ -2639,7 +2730,7 @@ var app9 = new Hono11().post("/:name/avatar", requireSelf(), async (c) => {
|
|
|
2639
2730
|
if (oldAvatarPath) rmSync2(oldAvatarPath, { force: true });
|
|
2640
2731
|
}
|
|
2641
2732
|
mkdirSync3(homeDir, { recursive: true });
|
|
2642
|
-
writeFileSync3(avatarPath,
|
|
2733
|
+
writeFileSync3(avatarPath, buffer);
|
|
2643
2734
|
const profile = config2.profile ?? {};
|
|
2644
2735
|
profile.avatar = filename;
|
|
2645
2736
|
config2.profile = profile;
|
|
@@ -3468,7 +3559,7 @@ async function handleMindEvent(mind, event) {
|
|
|
3468
3559
|
if (!turnId) {
|
|
3469
3560
|
llog.warn(`skipping turn tracking for ${mind}: createTurn failed`);
|
|
3470
3561
|
} else {
|
|
3471
|
-
|
|
3562
|
+
publish3(mind, { mind, type: "turn_created", turnId });
|
|
3472
3563
|
if (event.session) await assignSession(mind, turnId, event.session);
|
|
3473
3564
|
try {
|
|
3474
3565
|
await linkPendingInbound(mind, turnId, event.channel);
|
|
@@ -3514,7 +3605,7 @@ async function handleMindEvent(mind, event) {
|
|
|
3514
3605
|
llog.error("failed to link tool_result to turn", logger_default.errorData(err));
|
|
3515
3606
|
}
|
|
3516
3607
|
}
|
|
3517
|
-
|
|
3608
|
+
publish3(mind, {
|
|
3518
3609
|
mind,
|
|
3519
3610
|
type: event.type,
|
|
3520
3611
|
session: event.session,
|
|
@@ -3936,7 +4027,7 @@ async function getMindStatus(name, port, registryRunning) {
|
|
|
3936
4027
|
const manager = getMindManager();
|
|
3937
4028
|
let status = "stopped";
|
|
3938
4029
|
try {
|
|
3939
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4030
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
|
|
3940
4031
|
if (getSleepManagerIfReady2()?.isSleeping(name)) {
|
|
3941
4032
|
status = "sleeping";
|
|
3942
4033
|
}
|
|
@@ -4513,7 +4604,7 @@ var app13 = new Hono16().post("/", requireAdminOrSystem, zValidator6("json", cre
|
|
|
4513
4604
|
}
|
|
4514
4605
|
if (body.stage === "seed") {
|
|
4515
4606
|
const descLine = body.description ? `
|
|
4516
|
-
|
|
4607
|
+
Your creator described you as: "${body.description}"
|
|
4517
4608
|
` : "";
|
|
4518
4609
|
const seedSoulRaw = body.seedSoul ?? await getPrompt("seed_soul", { name, description: descLine });
|
|
4519
4610
|
const seedSoul = body.seedSoul ? substitute(seedSoulRaw, { name, description: descLine }) : seedSoulRaw;
|
|
@@ -4539,7 +4630,7 @@ The human who planted you described you as: "${body.description}"
|
|
|
4539
4630
|
try {
|
|
4540
4631
|
const spiritEntry = await findMind("volute");
|
|
4541
4632
|
if (spiritEntry) {
|
|
4542
|
-
const { spiritDir } = await import("./spirit-
|
|
4633
|
+
const { spiritDir } = await import("./spirit-RPBLLUQ5.js");
|
|
4543
4634
|
const sDir = spiritEntry.dir ?? spiritDir();
|
|
4544
4635
|
const spiritConfig = readVoluteConfig(sDir) ?? {};
|
|
4545
4636
|
const schedules = spiritConfig.schedules ?? [];
|
|
@@ -4554,7 +4645,7 @@ The human who planted you described you as: "${body.description}"
|
|
|
4554
4645
|
});
|
|
4555
4646
|
spiritConfig.schedules = schedules;
|
|
4556
4647
|
writeVoluteConfig(sDir, spiritConfig);
|
|
4557
|
-
const { getScheduler: getScheduler2 } = await import("./scheduler-
|
|
4648
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-WVKII3LA.js");
|
|
4558
4649
|
getScheduler2().loadSchedules("volute", sDir);
|
|
4559
4650
|
}
|
|
4560
4651
|
}
|
|
@@ -4820,7 +4911,7 @@ ${user.trimEnd()}
|
|
|
4820
4911
|
const manager = getMindManager();
|
|
4821
4912
|
try {
|
|
4822
4913
|
if (context?.type === "reload") {
|
|
4823
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4914
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
|
|
4824
4915
|
const sleepState = getSleepManagerIfReady2()?.getState(name);
|
|
4825
4916
|
if (sleepState?.sleeping) {
|
|
4826
4917
|
logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
|
|
@@ -4947,7 +5038,7 @@ ${user.trimEnd()}
|
|
|
4947
5038
|
const name = c.req.param("name");
|
|
4948
5039
|
const entry = await findMind(name);
|
|
4949
5040
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4950
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5041
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
|
|
4951
5042
|
const sm = getSleepManagerIfReady2();
|
|
4952
5043
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4953
5044
|
return c.json(sm.getState(name));
|
|
@@ -4955,7 +5046,7 @@ ${user.trimEnd()}
|
|
|
4955
5046
|
const name = c.req.param("name");
|
|
4956
5047
|
const entry = await findMind(name);
|
|
4957
5048
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4958
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5049
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
|
|
4959
5050
|
const sm = getSleepManagerIfReady2();
|
|
4960
5051
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4961
5052
|
if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
|
|
@@ -4975,7 +5066,7 @@ ${user.trimEnd()}
|
|
|
4975
5066
|
const name = c.req.param("name");
|
|
4976
5067
|
const entry = await findMind(name);
|
|
4977
5068
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4978
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5069
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
|
|
4979
5070
|
const sm = getSleepManagerIfReady2();
|
|
4980
5071
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4981
5072
|
const sleepState = sm.getState(name);
|
|
@@ -4990,7 +5081,7 @@ ${user.trimEnd()}
|
|
|
4990
5081
|
const name = c.req.param("name");
|
|
4991
5082
|
const entry = await findMind(name);
|
|
4992
5083
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
4993
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
5084
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-G6TXSDP7.js");
|
|
4994
5085
|
const sm = getSleepManagerIfReady2();
|
|
4995
5086
|
if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
|
|
4996
5087
|
const flushed = await sm.flushQueuedMessages(name);
|
|
@@ -5018,7 +5109,7 @@ ${user.trimEnd()}
|
|
|
5018
5109
|
}
|
|
5019
5110
|
config2.profile = profile;
|
|
5020
5111
|
writeVoluteConfig(dir, config2);
|
|
5021
|
-
const { syncMindProfile: syncMindProfile2 } = await import("./auth-
|
|
5112
|
+
const { syncMindProfile: syncMindProfile2 } = await import("./auth-CCFAU4GQ.js");
|
|
5022
5113
|
await syncMindProfile2(name, profile);
|
|
5023
5114
|
broadcast({ type: "profile_updated", mind: name, summary: `${name} profile updated` });
|
|
5024
5115
|
return c.json({ ok: true });
|
|
@@ -5028,7 +5119,6 @@ ${user.trimEnd()}
|
|
|
5028
5119
|
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
5029
5120
|
if (entry.stage !== "seed") return c.json({ output: "" });
|
|
5030
5121
|
const db = await getDb();
|
|
5031
|
-
const ORIENTATION_MARKER = "You don't have a soul yet";
|
|
5032
5122
|
const rawCreator = Number(process.env.VOLUTE_NURTURE_CREATOR_MINUTES);
|
|
5033
5123
|
const creatorThreshold = Number.isNaN(rawCreator) ? 5 : rawCreator;
|
|
5034
5124
|
const rawSpirit = Number(process.env.VOLUTE_NURTURE_SPIRIT_MINUTES);
|
|
@@ -5103,7 +5193,7 @@ ${user.trimEnd()}
|
|
|
5103
5193
|
try {
|
|
5104
5194
|
const spiritEntry = await findMind("volute");
|
|
5105
5195
|
if (spiritEntry) {
|
|
5106
|
-
const { spiritDir } = await import("./spirit-
|
|
5196
|
+
const { spiritDir } = await import("./spirit-RPBLLUQ5.js");
|
|
5107
5197
|
const sDir = spiritEntry.dir ?? spiritDir();
|
|
5108
5198
|
const spiritConfig = readVoluteConfig(sDir);
|
|
5109
5199
|
if (spiritConfig?.schedules) {
|
|
@@ -5111,7 +5201,7 @@ ${user.trimEnd()}
|
|
|
5111
5201
|
spiritConfig.schedules = spiritConfig.schedules.filter((s) => s.id !== nurtureId);
|
|
5112
5202
|
if (spiritConfig.schedules.length === 0) spiritConfig.schedules = void 0;
|
|
5113
5203
|
writeVoluteConfig(sDir, spiritConfig);
|
|
5114
|
-
const { getScheduler: getScheduler2 } = await import("./scheduler-
|
|
5204
|
+
const { getScheduler: getScheduler2 } = await import("./scheduler-WVKII3LA.js");
|
|
5115
5205
|
getScheduler2().loadSchedules("volute", sDir);
|
|
5116
5206
|
}
|
|
5117
5207
|
}
|
|
@@ -5546,7 +5636,7 @@ ${user.trimEnd()}
|
|
|
5546
5636
|
}
|
|
5547
5637
|
writeVoluteConfig(dir, existing);
|
|
5548
5638
|
if (body.unescapeNewlines !== void 0) {
|
|
5549
|
-
const { clearEchoTextCache } = await import("./echo-text-
|
|
5639
|
+
const { clearEchoTextCache } = await import("./echo-text-VQPB5L2X.js");
|
|
5550
5640
|
clearEchoTextCache(name);
|
|
5551
5641
|
}
|
|
5552
5642
|
const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.maxThinkingTokens !== void 0 || body.compaction !== void 0;
|
|
@@ -6000,76 +6090,93 @@ var slog2 = logger_default.child("schedules");
|
|
|
6000
6090
|
function readSchedules(dir) {
|
|
6001
6091
|
return readVoluteConfig(dir)?.schedules ?? [];
|
|
6002
6092
|
}
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
config2.schedules = schedules.length > 0 ? schedules : void 0;
|
|
6006
|
-
writeVoluteConfig(dir, config2);
|
|
6007
|
-
getScheduler().loadSchedules(name, dir);
|
|
6008
|
-
getSleepManagerIfReady()?.invalidateSleepConfig(name);
|
|
6009
|
-
fireWebhook({
|
|
6010
|
-
event: "schedule_changed",
|
|
6011
|
-
mind: name,
|
|
6012
|
-
data: { schedules }
|
|
6013
|
-
});
|
|
6014
|
-
}
|
|
6015
|
-
var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) => {
|
|
6016
|
-
const name = c.req.param("name");
|
|
6017
|
-
const entry = await findMind(name);
|
|
6018
|
-
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
6019
|
-
const dir = entry.dir ?? mindDir(name);
|
|
6020
|
-
const sleepManager = getSleepManagerIfReady();
|
|
6021
|
-
const sleepState = sleepManager?.getState(name) ?? null;
|
|
6022
|
-
const sleepConfig = sleepManager?.getSleepConfig(name) ?? null;
|
|
6023
|
-
const schedules = readSchedules(dir);
|
|
6024
|
-
const now = /* @__PURE__ */ new Date();
|
|
6093
|
+
var UPCOMING_HORIZON_MS = 24 * 60 * 60 * 1e3;
|
|
6094
|
+
function computeClockEvents(schedules, sleepState, sleepConfig, now) {
|
|
6025
6095
|
const upcoming = [];
|
|
6026
6096
|
const previous = [];
|
|
6097
|
+
const effectiveWake = sleepState?.sleeping ? sleepState.scheduledWakeAt ?? sleepState.voluntaryWakeAt : null;
|
|
6098
|
+
const effectiveWakeDate = effectiveWake ? new Date(effectiveWake) : null;
|
|
6099
|
+
const sleepFate = (s, fireDate) => {
|
|
6100
|
+
if (!effectiveWakeDate || fireDate >= effectiveWakeDate) return {};
|
|
6101
|
+
const ws = s.whileSleeping ?? "queue";
|
|
6102
|
+
if (ws === "skip") return { willSkip: true };
|
|
6103
|
+
if (ws === "trigger-wake") return {};
|
|
6104
|
+
return { willQueue: true };
|
|
6105
|
+
};
|
|
6027
6106
|
for (const s of schedules) {
|
|
6028
6107
|
if (!s.enabled) continue;
|
|
6029
6108
|
if (s.fireAt) {
|
|
6030
6109
|
const fireDate = new Date(s.fireAt);
|
|
6031
6110
|
if (fireDate >= now) {
|
|
6032
|
-
upcoming.push({
|
|
6111
|
+
upcoming.push({
|
|
6112
|
+
id: s.id,
|
|
6113
|
+
at: fireDate.toISOString(),
|
|
6114
|
+
type: "timer",
|
|
6115
|
+
...sleepFate(s, fireDate)
|
|
6116
|
+
});
|
|
6033
6117
|
}
|
|
6034
6118
|
} else if (s.cron) {
|
|
6035
6119
|
try {
|
|
6036
|
-
const
|
|
6037
|
-
|
|
6038
|
-
upcoming.push({ id: s.id, at: next.toISOString(), type: "cron" });
|
|
6120
|
+
const next = CronExpressionParser3.parse(s.cron, { currentDate: now }).next().toDate();
|
|
6121
|
+
upcoming.push({ id: s.id, at: next.toISOString(), type: "cron", ...sleepFate(s, next) });
|
|
6039
6122
|
} catch {
|
|
6040
|
-
slog2.warn(`invalid cron "${s.cron}" for schedule "${s.id}"
|
|
6123
|
+
slog2.warn(`invalid cron "${s.cron}" for schedule "${s.id}"`);
|
|
6041
6124
|
}
|
|
6042
6125
|
try {
|
|
6043
|
-
const
|
|
6044
|
-
const prev = prevInterval.prev().toDate();
|
|
6126
|
+
const prev = CronExpressionParser3.parse(s.cron, { currentDate: now }).prev().toDate();
|
|
6045
6127
|
previous.push({ id: s.id, at: prev.toISOString() });
|
|
6046
6128
|
} catch {
|
|
6047
6129
|
}
|
|
6048
6130
|
}
|
|
6049
6131
|
}
|
|
6050
6132
|
if (sleepState?.sleeping) {
|
|
6051
|
-
if (
|
|
6052
|
-
upcoming.push({ id: "
|
|
6133
|
+
if (effectiveWake) {
|
|
6134
|
+
upcoming.push({ id: "wake", at: effectiveWake, type: "cron" });
|
|
6053
6135
|
}
|
|
6054
6136
|
if (sleepState.sleepingSince) {
|
|
6055
6137
|
previous.push({ id: "sleep", at: sleepState.sleepingSince });
|
|
6056
6138
|
}
|
|
6057
6139
|
} else if (sleepConfig?.enabled && sleepConfig.schedule) {
|
|
6058
6140
|
try {
|
|
6059
|
-
const
|
|
6060
|
-
const nextSleep = sleepInterval.next().toDate();
|
|
6141
|
+
const nextSleep = CronExpressionParser3.parse(sleepConfig.schedule.sleep, { currentDate: now }).next().toDate();
|
|
6061
6142
|
upcoming.push({ id: "sleep", at: nextSleep.toISOString(), type: "cron" });
|
|
6062
6143
|
} catch {
|
|
6063
6144
|
}
|
|
6064
6145
|
try {
|
|
6065
|
-
const
|
|
6066
|
-
|
|
6067
|
-
previous.push({ id: "sleep", at: prevWake.toISOString() });
|
|
6146
|
+
const prevWake = CronExpressionParser3.parse(sleepConfig.schedule.wake, { currentDate: now }).prev().toDate();
|
|
6147
|
+
previous.push({ id: "wake", at: prevWake.toISOString() });
|
|
6068
6148
|
} catch {
|
|
6069
6149
|
}
|
|
6070
6150
|
}
|
|
6071
|
-
|
|
6151
|
+
const horizon = now.getTime() + UPCOMING_HORIZON_MS;
|
|
6152
|
+
const withinHorizon = upcoming.filter((e) => new Date(e.at).getTime() <= horizon);
|
|
6153
|
+
withinHorizon.sort((a, b) => a.at.localeCompare(b.at));
|
|
6072
6154
|
previous.sort((a, b) => b.at.localeCompare(a.at));
|
|
6155
|
+
return { upcoming: withinHorizon, previous };
|
|
6156
|
+
}
|
|
6157
|
+
function writeSchedules(name, dir, schedules) {
|
|
6158
|
+
const config2 = readVoluteConfig(dir) ?? {};
|
|
6159
|
+
config2.schedules = schedules.length > 0 ? schedules : void 0;
|
|
6160
|
+
writeVoluteConfig(dir, config2);
|
|
6161
|
+
getScheduler().loadSchedules(name, dir);
|
|
6162
|
+
getSleepManagerIfReady()?.invalidateSleepConfig(name);
|
|
6163
|
+
fireWebhook({
|
|
6164
|
+
event: "schedule_changed",
|
|
6165
|
+
mind: name,
|
|
6166
|
+
data: { schedules }
|
|
6167
|
+
});
|
|
6168
|
+
}
|
|
6169
|
+
var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) => {
|
|
6170
|
+
const name = c.req.param("name");
|
|
6171
|
+
const entry = await findMind(name);
|
|
6172
|
+
if (!entry) return c.json({ error: "Mind not found" }, 404);
|
|
6173
|
+
const dir = entry.dir ?? mindDir(name);
|
|
6174
|
+
const sleepManager = getSleepManagerIfReady();
|
|
6175
|
+
const sleepState = sleepManager?.getState(name) ?? null;
|
|
6176
|
+
const sleepConfig = sleepManager?.getSleepConfig(name) ?? null;
|
|
6177
|
+
const schedules = readSchedules(dir);
|
|
6178
|
+
const now = /* @__PURE__ */ new Date();
|
|
6179
|
+
const { upcoming, previous } = computeClockEvents(schedules, sleepState, sleepConfig, now);
|
|
6073
6180
|
return c.json({ sleep: sleepState, sleepConfig, schedules, upcoming, previous });
|
|
6074
6181
|
}).get("/:name/sleep/config", requireSelf(), async (c) => {
|
|
6075
6182
|
const name = c.req.param("name");
|
|
@@ -6236,7 +6343,7 @@ var app15 = new Hono18().get("/:name/clock/status", requireSelf(), async (c) =>
|
|
|
6236
6343
|
const body = await c.req.text();
|
|
6237
6344
|
const message = `[webhook: ${event}] ${body}`;
|
|
6238
6345
|
try {
|
|
6239
|
-
const { sendSystemMessage } = await import("./system-chat-
|
|
6346
|
+
const { sendSystemMessage } = await import("./system-chat-NDAW6YP7.js");
|
|
6240
6347
|
await sendSystemMessage(name, message);
|
|
6241
6348
|
return c.json({ ok: true });
|
|
6242
6349
|
} catch (err) {
|
|
@@ -6290,7 +6397,7 @@ setup.get("/status", async (c) => {
|
|
|
6290
6397
|
let hasAccount = false;
|
|
6291
6398
|
if (hasSystem) {
|
|
6292
6399
|
try {
|
|
6293
|
-
const { listUsersByType: listUsersByType2 } = await import("./auth-
|
|
6400
|
+
const { listUsersByType: listUsersByType2 } = await import("./auth-CCFAU4GQ.js");
|
|
6294
6401
|
const brains = await listUsersByType2("brain");
|
|
6295
6402
|
hasAccount = brains.length > 0;
|
|
6296
6403
|
} catch (err) {
|
|
@@ -6501,7 +6608,7 @@ setup.post("/account", async (c) => {
|
|
|
6501
6608
|
}
|
|
6502
6609
|
}
|
|
6503
6610
|
try {
|
|
6504
|
-
const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-
|
|
6611
|
+
const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-CCFAU4GQ.js");
|
|
6505
6612
|
const user = await createUser2(body.username.trim(), body.password);
|
|
6506
6613
|
if (body.displayName?.trim()) {
|
|
6507
6614
|
await updateUserProfile2(user.id, { display_name: body.displayName.trim() });
|
|
@@ -6565,8 +6672,8 @@ setup.post("/complete", async (c) => {
|
|
|
6565
6672
|
return c.json({ error: "Setup already complete" }, 400);
|
|
6566
6673
|
}
|
|
6567
6674
|
try {
|
|
6568
|
-
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-
|
|
6569
|
-
const { startSpiritFull } = await import("./mind-service-
|
|
6675
|
+
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-RPBLLUQ5.js");
|
|
6676
|
+
const { startSpiritFull } = await import("./mind-service-NB2JMWQZ.js");
|
|
6570
6677
|
await ensureSpiritProject();
|
|
6571
6678
|
await syncSpiritTemplate();
|
|
6572
6679
|
const warnings = [];
|
|
@@ -6601,8 +6708,8 @@ setup.post("/complete", async (c) => {
|
|
|
6601
6708
|
}
|
|
6602
6709
|
let spiritConversationId;
|
|
6603
6710
|
try {
|
|
6604
|
-
const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-
|
|
6605
|
-
const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-
|
|
6711
|
+
const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-CCFAU4GQ.js");
|
|
6712
|
+
const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-CJOOCGNW.js");
|
|
6606
6713
|
const spiritUser = await getOrCreateMindUser2("volute");
|
|
6607
6714
|
const brains = await listUsersByType2("brain");
|
|
6608
6715
|
const admin2 = brains.find((u) => u.role === "admin");
|
|
@@ -6624,21 +6731,23 @@ setup.post("/complete", async (c) => {
|
|
|
6624
6731
|
logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
|
|
6625
6732
|
if (spiritConversationId && spiritStarted) {
|
|
6626
6733
|
try {
|
|
6627
|
-
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-
|
|
6628
|
-
const { listUsersByType: listUsers2 } = await import("./auth-
|
|
6734
|
+
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-R7JSQGYX.js");
|
|
6735
|
+
const { listUsersByType: listUsers2 } = await import("./auth-CCFAU4GQ.js");
|
|
6629
6736
|
const admins = await listUsers2("brain");
|
|
6630
6737
|
const admin2 = admins.find((u) => u.role === "admin");
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6738
|
+
if (admin2) {
|
|
6739
|
+
const adminName = admin2.display_name || admin2.username;
|
|
6740
|
+
const welcomePrompt = `Setup is complete. ${adminName} just finished setting up this system and is now in your chat. Welcome them and help them create their first mind. Keep it brief \u2014 a short greeting, 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.`;
|
|
6741
|
+
await deliverMessage2(SPIRIT_NAME, {
|
|
6742
|
+
content: [{ type: "text", text: welcomePrompt }],
|
|
6743
|
+
channel: `@${admin2.username}`,
|
|
6744
|
+
conversationId: spiritConversationId,
|
|
6745
|
+
sender: admin2.username,
|
|
6746
|
+
isDM: true,
|
|
6747
|
+
participants: [SPIRIT_NAME, admin2.username],
|
|
6748
|
+
participantCount: 2
|
|
6749
|
+
});
|
|
6750
|
+
}
|
|
6642
6751
|
} catch (err) {
|
|
6643
6752
|
logger_default.warn("failed to send welcome prompt to spirit (non-fatal)", logger_default.errorData(err));
|
|
6644
6753
|
warnings.push(
|
|
@@ -6735,10 +6844,10 @@ var app16 = new Hono20().get("/", async (c) => {
|
|
|
6735
6844
|
if (!file.name.endsWith(".zip")) {
|
|
6736
6845
|
return c.json({ error: "Only .zip files are accepted" }, 400);
|
|
6737
6846
|
}
|
|
6738
|
-
const
|
|
6847
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
6739
6848
|
const tmpDir = mkdtempSync(join(tmpdir(), "volute-skill-upload-"));
|
|
6740
6849
|
try {
|
|
6741
|
-
const zip = new AdmZip(
|
|
6850
|
+
const zip = new AdmZip(buffer);
|
|
6742
6851
|
for (const entry of zip.getEntries()) {
|
|
6743
6852
|
const target = resolve12(tmpDir, entry.entryName);
|
|
6744
6853
|
if (!target.startsWith(tmpDir)) {
|
|
@@ -6808,7 +6917,7 @@ var app17 = new Hono21().post("/:name/typing", zValidator8("json", typingSchema)
|
|
|
6808
6917
|
map.delete(channel, sender);
|
|
6809
6918
|
}
|
|
6810
6919
|
if (isConversationId(channel)) {
|
|
6811
|
-
|
|
6920
|
+
publish2(channel, { type: "typing", senders: map.get(channel) });
|
|
6812
6921
|
}
|
|
6813
6922
|
return c.json({ ok: true });
|
|
6814
6923
|
}).get("/:name/typing", (c) => {
|
|
@@ -6971,42 +7080,6 @@ function getOnlineBrains() {
|
|
|
6971
7080
|
return [...connections.keys()];
|
|
6972
7081
|
}
|
|
6973
7082
|
|
|
6974
|
-
// packages/daemon/src/lib/events/event-sequencer.ts
|
|
6975
|
-
var BUFFER_SIZE = 1e3;
|
|
6976
|
-
var MAX_AGE_MS = 5 * 60 * 1e3;
|
|
6977
|
-
var nextId = 1;
|
|
6978
|
-
var buffer = [];
|
|
6979
|
-
function bufferEvent(data) {
|
|
6980
|
-
const id = nextId++;
|
|
6981
|
-
buffer.push({ id, data, timestamp: Date.now() });
|
|
6982
|
-
while (buffer.length > BUFFER_SIZE) {
|
|
6983
|
-
buffer.shift();
|
|
6984
|
-
}
|
|
6985
|
-
return id;
|
|
6986
|
-
}
|
|
6987
|
-
function nextEventId() {
|
|
6988
|
-
return nextId++;
|
|
6989
|
-
}
|
|
6990
|
-
function getEventsSince(sinceId, allowedConversationIds, allowedActivityMind) {
|
|
6991
|
-
const now = Date.now();
|
|
6992
|
-
const startIdx = buffer.findIndex((e) => e.id > sinceId);
|
|
6993
|
-
if (startIdx === -1) return [];
|
|
6994
|
-
return buffer.slice(startIdx).filter((e) => {
|
|
6995
|
-
if (now - e.timestamp >= MAX_AGE_MS) return false;
|
|
6996
|
-
const data = e.data;
|
|
6997
|
-
switch (data.event) {
|
|
6998
|
-
case "snapshot":
|
|
6999
|
-
return false;
|
|
7000
|
-
case "conversation":
|
|
7001
|
-
return allowedConversationIds.has(data.conversationId);
|
|
7002
|
-
case "activity":
|
|
7003
|
-
return allowedActivityMind === void 0 || data.mind === allowedActivityMind;
|
|
7004
|
-
default:
|
|
7005
|
-
return false;
|
|
7006
|
-
}
|
|
7007
|
-
});
|
|
7008
|
-
}
|
|
7009
|
-
|
|
7010
7083
|
// packages/daemon/src/web/api/v1/events.ts
|
|
7011
7084
|
var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
7012
7085
|
const user = c.get("user");
|
|
@@ -7068,15 +7141,15 @@ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7068
7141
|
data: JSON.stringify(snapshotData)
|
|
7069
7142
|
});
|
|
7070
7143
|
const unsubActivity = subscribe((event) => {
|
|
7144
|
+
if (activityMind !== void 0 && event.mind !== activityMind) return;
|
|
7145
|
+
const { seq, ...rest } = event;
|
|
7071
7146
|
const data = {
|
|
7072
7147
|
event: "activity",
|
|
7073
|
-
...
|
|
7074
|
-
metadata:
|
|
7148
|
+
...rest,
|
|
7149
|
+
metadata: rest.metadata ?? null
|
|
7075
7150
|
};
|
|
7076
|
-
const eventId = bufferEvent(data);
|
|
7077
|
-
if (activityMind !== void 0 && event.mind !== activityMind) return;
|
|
7078
7151
|
stream.writeSSE({
|
|
7079
|
-
id: String(
|
|
7152
|
+
id: String(seq),
|
|
7080
7153
|
data: JSON.stringify(data)
|
|
7081
7154
|
}).catch((err) => {
|
|
7082
7155
|
if (!stream.aborted) logger_default.error("[v1-events] write error:", logger_default.errorData(err));
|
|
@@ -7085,10 +7158,10 @@ var app20 = new Hono24().use("*", authMiddleware).get("/", async (c) => {
|
|
|
7085
7158
|
cleanups.push(unsubActivity);
|
|
7086
7159
|
for (const conv of conversations2) {
|
|
7087
7160
|
const unsubConv = subscribe2(conv.id, (event) => {
|
|
7088
|
-
const
|
|
7089
|
-
const
|
|
7161
|
+
const { seq, ...rest } = event;
|
|
7162
|
+
const data = { event: "conversation", conversationId: conv.id, ...rest };
|
|
7090
7163
|
stream.writeSSE({
|
|
7091
|
-
id: String(
|
|
7164
|
+
id: String(seq),
|
|
7092
7165
|
data: JSON.stringify(data)
|
|
7093
7166
|
}).catch((err) => {
|
|
7094
7167
|
if (!stream.aborted) logger_default.error("[v1-events] write error:", logger_default.errorData(err));
|
|
@@ -7126,12 +7199,9 @@ import { Hono as Hono25 } from "hono";
|
|
|
7126
7199
|
import { spawn as spawn4 } from "child_process";
|
|
7127
7200
|
import { closeSync, mkdirSync as mkdirSync8, openSync, readFileSync as readFileSync9 } from "fs";
|
|
7128
7201
|
import { resolve as resolve13 } from "path";
|
|
7129
|
-
function tsxBin(cwd) {
|
|
7130
|
-
return resolve13(cwd, "node_modules", ".bin", "tsx");
|
|
7131
|
-
}
|
|
7132
7202
|
async function spawnServer(cwd, port, options) {
|
|
7133
|
-
let cmd =
|
|
7134
|
-
let args = ["src/server.ts", "--port", String(port)];
|
|
7203
|
+
let cmd = process.env.VOLUTE_NODE_PATH ?? process.execPath;
|
|
7204
|
+
let args = ["--import", "tsx", "src/server.ts", "--port", String(port)];
|
|
7135
7205
|
if (options?.mindName) {
|
|
7136
7206
|
if (isIsolationEnabled()) {
|
|
7137
7207
|
[cmd, args] = await wrapForIsolation(cmd, args, options.mindName);
|
|
@@ -7834,7 +7904,7 @@ var unifiedChatApp = new Hono27().post(
|
|
|
7834
7904
|
});
|
|
7835
7905
|
if (outboundTurnId) {
|
|
7836
7906
|
const mindKey = senderBase ?? senderName;
|
|
7837
|
-
|
|
7907
|
+
publish3(mindKey, {
|
|
7838
7908
|
mind: mindKey,
|
|
7839
7909
|
type: "outbound",
|
|
7840
7910
|
channel,
|
|
@@ -7960,6 +8030,9 @@ var app24 = new Hono28().get("/:name/conversations", async (c) => {
|
|
|
7960
8030
|
if (!u) return c.json({ error: `User ${id} not found` }, 400);
|
|
7961
8031
|
}
|
|
7962
8032
|
const participantIds = [...participantSet];
|
|
8033
|
+
if (participantIds.length < 2) {
|
|
8034
|
+
return c.json({ error: "a conversation needs at least two distinct participants" }, 400);
|
|
8035
|
+
}
|
|
7963
8036
|
if (participantIds.length > 2) {
|
|
7964
8037
|
return c.json({ error: "Use channels for multi-participant conversations" }, 400);
|
|
7965
8038
|
}
|
|
@@ -8290,7 +8363,7 @@ async function startDaemon(opts) {
|
|
|
8290
8363
|
logger_default.warn("failed to ensure #system channel", logger_default.errorData(err));
|
|
8291
8364
|
}
|
|
8292
8365
|
try {
|
|
8293
|
-
const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-
|
|
8366
|
+
const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-CCFAU4GQ.js");
|
|
8294
8367
|
await getOrCreateSystemUser2();
|
|
8295
8368
|
} catch (err) {
|
|
8296
8369
|
logger_default.warn(
|
|
@@ -8386,13 +8459,17 @@ async function startDaemon(opts) {
|
|
|
8386
8459
|
try {
|
|
8387
8460
|
const { isSetupComplete: isSetupComplete2 } = await import("./setup-Y2EJI3GG.js");
|
|
8388
8461
|
if (isSetupComplete2()) {
|
|
8389
|
-
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-
|
|
8390
|
-
const { startSpiritFull } = await import("./mind-service-
|
|
8462
|
+
const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-RPBLLUQ5.js");
|
|
8463
|
+
const { startSpiritFull } = await import("./mind-service-NB2JMWQZ.js");
|
|
8391
8464
|
await ensureSpiritProject();
|
|
8465
|
+
const spiritEntry = await findMind(SPIRIT_NAME);
|
|
8466
|
+
if (spiritEntry?.dir) {
|
|
8467
|
+
const { registerMindDir } = await import("./delivery-router-N4A7ZC5T.js");
|
|
8468
|
+
registerMindDir(SPIRIT_NAME, spiritEntry.dir);
|
|
8469
|
+
}
|
|
8392
8470
|
await syncSpiritTemplate();
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
await startSpiritFull("volute");
|
|
8471
|
+
if (spiritEntry && !manager.isRunning(SPIRIT_NAME)) {
|
|
8472
|
+
await startSpiritFull(SPIRIT_NAME);
|
|
8396
8473
|
}
|
|
8397
8474
|
}
|
|
8398
8475
|
} catch (err) {
|
|
@@ -8404,7 +8481,7 @@ async function startDaemon(opts) {
|
|
|
8404
8481
|
bridgeManager.startBridges(daemonPort).catch((err) => {
|
|
8405
8482
|
logger_default.warn("failed to start bridges", logger_default.errorData(err));
|
|
8406
8483
|
});
|
|
8407
|
-
import("./cloud-sync-
|
|
8484
|
+
import("./cloud-sync-46RS4SDA.js").then(
|
|
8408
8485
|
({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
|
|
8409
8486
|
logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
|
|
8410
8487
|
})
|
|
@@ -8412,7 +8489,7 @@ async function startDaemon(opts) {
|
|
|
8412
8489
|
logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
|
|
8413
8490
|
});
|
|
8414
8491
|
try {
|
|
8415
|
-
const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-
|
|
8492
|
+
const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-C5CH7CVP.js");
|
|
8416
8493
|
backfillTemplateHashes();
|
|
8417
8494
|
notifyVersionUpdate().catch((err) => {
|
|
8418
8495
|
logger_default.warn("failed to send version update notifications", logger_default.errorData(err));
|
|
@@ -8430,6 +8507,7 @@ async function startDaemon(opts) {
|
|
|
8430
8507
|
cleanExpiredLogs().catch((err) => {
|
|
8431
8508
|
logger_default.warn("failed to clean expired logs", logger_default.errorData(err));
|
|
8432
8509
|
});
|
|
8510
|
+
const maintenanceInterval = startMaintenanceInterval();
|
|
8433
8511
|
setProviderRefreshHook((provider) => {
|
|
8434
8512
|
void syncProviderToMinds(provider, {
|
|
8435
8513
|
listRunning: () => getMindManager().getRunningMinds()
|
|
@@ -8482,6 +8560,7 @@ async function startDaemon(opts) {
|
|
|
8482
8560
|
safe("summarizer.stop", () => summarizer.stop());
|
|
8483
8561
|
safe("backupManager.stop", () => backupManager2.stop());
|
|
8484
8562
|
safe("stopApiKeyRefresh", stopApiKeyRefresh);
|
|
8563
|
+
safe("maintenanceInterval", () => clearInterval(maintenanceInterval));
|
|
8485
8564
|
safe("delivery.dispose", () => delivery.dispose());
|
|
8486
8565
|
await safe("bridgeManager.stopAll", () => bridgeManager.stopAll());
|
|
8487
8566
|
await safe("manager.stopAll", () => manager.stopAll());
|