volute 0.44.0 → 0.45.1
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/{ai-service-7PHSRDMO.js → ai-service-534WUSDL.js} +2 -2
- package/dist/backup-OZXHL3JS.js +381 -0
- package/dist/{chunk-PNV24SEP.js → chunk-322KE457.js} +13 -1
- package/dist/{chunk-7FT24JBQ.js → chunk-5AUKCNPE.js} +5 -5
- package/dist/{chunk-EQZD4YU3.js → chunk-77BVGCTZ.js} +3 -3
- package/dist/{chunk-7KWANSLX.js → chunk-IUXTYXFI.js} +1 -1
- package/dist/{chunk-FZMEVCWC.js → chunk-JSRMEY52.js} +4 -2
- package/dist/chunk-KVKP6VQ5.js +318 -0
- package/dist/{chunk-3GYVW2X3.js → chunk-OXXTPILE.js} +1 -1
- package/dist/{chunk-J47TIYHA.js → chunk-TSK5PI4L.js} +1 -1
- package/dist/{chunk-OMETFX5H.js → chunk-U4FNXDEJ.js} +47 -21
- package/dist/{chunk-NF75HPNR.js → chunk-V4GYPD5L.js} +39 -14
- package/dist/{chunk-I2Y2YHIO.js → chunk-Y6OPHHGM.js} +2 -2
- package/dist/{chunk-5AHYQHNT.js → chunk-ZIR7HSAX.js} +2 -1
- package/dist/cli.js +13 -7
- package/dist/{cloud-sync-6WJ7WFYD.js → cloud-sync-Y7LQPRE3.js} +9 -9
- package/dist/{conversations-WAJYZKOC.js → conversations-G4LYMHAA.js} +1 -1
- package/dist/{create-YZIV5OJC.js → create-2YY7IPMS.js} +1 -1
- package/dist/{daemon-restart-OUJVINXV.js → daemon-restart-ABP54YN6.js} +2 -2
- package/dist/daemon.js +426 -228
- package/dist/{delivery-manager-TEVOHX5W.js → delivery-manager-R5STGLXB.js} +9 -9
- package/dist/{delivery-router-GODSIS62.js → delivery-router-6DE3BPHO.js} +1 -1
- package/dist/{echo-text-T7ZA5EYD.js → echo-text-NQXYYYCU.js} +10 -10
- package/dist/{extensions-3QNDGW5E.js → extensions-AYSPKE33.js} +9 -9
- package/dist/{message-delivery-2EA5LLMD.js → message-delivery-IUAXRPFE.js} +9 -9
- package/dist/{mind-MI7Z6KM2.js → mind-KLHC7XYR.js} +4 -4
- package/dist/{mind-service-S3SY7HAM.js → mind-service-BF2LUQMH.js} +9 -9
- package/dist/{package-5B4XDTJH.js → package-BMHV7UHN.js} +1 -1
- package/dist/{sandbox-IOPWWILM.js → sandbox-72MWV4JL.js} +2 -2
- package/dist/{scheduler-CXWQ6Z4E.js → scheduler-YHJRRV6E.js} +9 -9
- package/dist/{seed-UUPVQWFB.js → seed-4MKQ5QKG.js} +1 -1
- package/dist/{seed-cmd-7XPKDQUC.js → seed-cmd-LCXMR7VV.js} +2 -2
- package/dist/{seed-create-IZ4M5ESO.js → seed-create-N2BF5LQX.js} +3 -3
- package/dist/{seed-sprout-POJXHJWV.js → seed-sprout-6ZDP23YI.js} +4 -4
- package/dist/{setup-5GCO3CUB.js → setup-3O6O2JZ7.js} +2 -2
- package/dist/{setup-MLRKNP7Y.js → setup-73YBJZQL.js} +1 -1
- package/dist/skills/memory/SKILL.md +2 -1
- package/dist/skills/orientation/SKILL.md +1 -1
- package/dist/skills/plan-coordinator/SKILL.md +2 -2
- package/dist/skills/volute-admin/SKILL.md +9 -6
- package/dist/skills/volute-mind/SKILL.md +15 -16
- package/dist/skills/volute-mind/references/extensions.md +5 -3
- package/dist/skills/volute-mind/references/integrations.md +2 -2
- package/dist/skills/volute-mind/references/routing.md +19 -23
- package/dist/skills/volute-mind/references/variants.md +10 -8
- package/dist/{skills-775C7QFF.js → skills-ND4ZFYUW.js} +2 -2
- package/dist/{sleep-manager-HINI6Y46.js → sleep-manager-YAHDOIU5.js} +9 -9
- package/dist/{spirit-IK5OYWMV.js → spirit-II3HOERM.js} +4 -4
- package/dist/{sprout-ZY5QGOEL.js → sprout-4KUG76AQ.js} +1 -1
- package/dist/{system-chat-4DQZOEZI.js → system-chat-E65T2HSW.js} +9 -9
- package/dist/{turn-tracker-RWZMY7M6.js → turn-tracker-QQIVVXBI.js} +5 -3
- package/dist/{up-FLCDR2UB.js → up-LG56MPBI.js} +2 -2
- package/dist/{upgrade-37DAGF6H.js → upgrade-NWAZ7MLD.js} +1 -1
- package/dist/{version-notify-B67N35A3.js → version-notify-CODJOUW2.js} +9 -9
- package/dist/web-assets/assets/{index-dJdNzAPn.css → index-B1JBQv2m.css} +1 -1
- package/dist/web-assets/assets/index-Dac11sKB.js +86 -0
- package/dist/web-assets/index.html +2 -2
- package/package.json +1 -1
- package/packages/extensions/plan/skills/plan/SKILL.md +1 -1
- package/templates/_base/.init/.config/prompts.json +1 -1
- package/templates/_base/.init/.local/hooks/startup-context.ts +0 -8
- package/templates/_base/.init/MEMORY.md +2 -1
- package/templates/_base/home/VOLUTE.md +6 -9
- package/templates/_base/src/lib/startup.ts +1 -1
- package/templates/claude/.init/CLAUDE.md +1 -1
- package/templates/claude/src/agent.ts +22 -2
- package/templates/claude/src/lib/stream-consumer.ts +7 -4
- package/templates/codex/.init/AGENTS.md +4 -2
- package/templates/codex/src/agent.ts +13 -7
- package/templates/pi/.init/MINDS.md +4 -2
- package/templates/pi/src/lib/event-handler.ts +6 -2
- package/dist/web-assets/assets/index-Domoz5Cg.js +0 -79
- package/templates/_base/.init/.config/hooks/pre-prompt/session-activity.ts +0 -40
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
exec
|
|
4
|
+
} from "./chunk-3ERIL673.js";
|
|
5
|
+
import {
|
|
6
|
+
readGlobalConfig
|
|
7
|
+
} from "./chunk-322KE457.js";
|
|
8
|
+
import {
|
|
9
|
+
logger_default
|
|
10
|
+
} from "./chunk-T2TP6ZC6.js";
|
|
11
|
+
import {
|
|
12
|
+
getDb,
|
|
13
|
+
readAllMinds,
|
|
14
|
+
voluteHome,
|
|
15
|
+
voluteSystemDir
|
|
16
|
+
} from "./chunk-H7IYFRAL.js";
|
|
17
|
+
|
|
18
|
+
// packages/daemon/src/lib/backup/restic.ts
|
|
19
|
+
import { writeFileSync } from "fs";
|
|
20
|
+
import { resolve } from "path";
|
|
21
|
+
var RESTIC_INSTALL_HINT = "Install restic: `brew install restic` (macOS), `apt-get install restic` (Debian/Ubuntu), or see https://restic.net";
|
|
22
|
+
async function resticVersion() {
|
|
23
|
+
try {
|
|
24
|
+
const out = await exec("restic", ["version"]);
|
|
25
|
+
return out.trim();
|
|
26
|
+
} catch (err) {
|
|
27
|
+
if (err.code !== "ENOENT") {
|
|
28
|
+
logger_default.child("backup").warn("restic exists but failed to run", logger_default.errorData(err));
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function resticEnv(config) {
|
|
34
|
+
if (!config.repository) throw new Error("Backup repository is not configured");
|
|
35
|
+
if (!config.password) throw new Error("Backup password is not configured");
|
|
36
|
+
return {
|
|
37
|
+
...process.env,
|
|
38
|
+
...config.env,
|
|
39
|
+
RESTIC_REPOSITORY: config.repository,
|
|
40
|
+
RESTIC_PASSWORD: config.password
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async function runRestic(args, config, options) {
|
|
44
|
+
if (!await resticVersion()) {
|
|
45
|
+
throw new Error(`restic is not installed. ${RESTIC_INSTALL_HINT}`);
|
|
46
|
+
}
|
|
47
|
+
return exec("restic", args, { cwd: options?.cwd, env: resticEnv(config) });
|
|
48
|
+
}
|
|
49
|
+
var BASE_EXCLUDES = [
|
|
50
|
+
// dependency trees and build output — rehydrated via lockfiles
|
|
51
|
+
"node_modules",
|
|
52
|
+
".venv",
|
|
53
|
+
"venv",
|
|
54
|
+
"__pycache__",
|
|
55
|
+
"dist",
|
|
56
|
+
// mind runtime state that is regenerated
|
|
57
|
+
".variants",
|
|
58
|
+
".worktrees",
|
|
59
|
+
".mind/tmp",
|
|
60
|
+
".npm",
|
|
61
|
+
// toolchains and caches minds install into their home. home/.local must NOT
|
|
62
|
+
// be excluded wholesale: .local/hooks and .local/bin hold mind-authored hooks
|
|
63
|
+
// and skill shims that are never regenerated — only its toolchain subdirs go.
|
|
64
|
+
"home/.cache",
|
|
65
|
+
"home/.npm",
|
|
66
|
+
"home/.rustup",
|
|
67
|
+
"home/.cargo",
|
|
68
|
+
"home/.local/share",
|
|
69
|
+
"home/.local/state",
|
|
70
|
+
"home/.local/lib",
|
|
71
|
+
"home/.local/pipx"
|
|
72
|
+
];
|
|
73
|
+
function systemExcludes() {
|
|
74
|
+
const sys = voluteSystemDir();
|
|
75
|
+
return [
|
|
76
|
+
resolve(sys, "volute.db"),
|
|
77
|
+
resolve(sys, "volute.db-wal"),
|
|
78
|
+
resolve(sys, "volute.db-shm"),
|
|
79
|
+
`${resolve(sys, "extension-data")}/*/data.db`,
|
|
80
|
+
`${resolve(sys, "extension-data")}/*/data.db-wal`,
|
|
81
|
+
`${resolve(sys, "extension-data")}/*/data.db-shm`,
|
|
82
|
+
`${resolve(sys, "state")}/*/logs`,
|
|
83
|
+
resolve(sys, "daemon.log"),
|
|
84
|
+
resolve(sys, "daemon.pid"),
|
|
85
|
+
resolve(sys, "backup-excludes.txt")
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
var SESSION_EXCLUDES = [
|
|
89
|
+
"home/.claude/projects",
|
|
90
|
+
"home/.claude/debug",
|
|
91
|
+
"home/.claude/telemetry",
|
|
92
|
+
"home/.claude/todos",
|
|
93
|
+
"home/.claude/session-env",
|
|
94
|
+
".mind/codex/sessions",
|
|
95
|
+
".mind/pi-sessions"
|
|
96
|
+
];
|
|
97
|
+
function buildExcludePatterns(config) {
|
|
98
|
+
const patterns = [...BASE_EXCLUDES, ...systemExcludes()];
|
|
99
|
+
if (!config.includeSessions) patterns.push(...SESSION_EXCLUDES);
|
|
100
|
+
return patterns;
|
|
101
|
+
}
|
|
102
|
+
function writeExcludeFile(config) {
|
|
103
|
+
const path = resolve(voluteSystemDir(), "backup-excludes.txt");
|
|
104
|
+
writeFileSync(path, `${buildExcludePatterns(config).join("\n")}
|
|
105
|
+
`);
|
|
106
|
+
return path;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// packages/daemon/src/lib/backup/backup.ts
|
|
110
|
+
import {
|
|
111
|
+
existsSync,
|
|
112
|
+
mkdirSync,
|
|
113
|
+
readdirSync,
|
|
114
|
+
readFileSync,
|
|
115
|
+
renameSync,
|
|
116
|
+
rmSync,
|
|
117
|
+
writeFileSync as writeFileSync2
|
|
118
|
+
} from "fs";
|
|
119
|
+
import { dirname, resolve as resolve2 } from "path";
|
|
120
|
+
import { sql } from "drizzle-orm";
|
|
121
|
+
var blog = logger_default.child("backup");
|
|
122
|
+
var DEFAULT_KEEP = { daily: 7, weekly: 4, monthly: 12 };
|
|
123
|
+
function stateFilePath() {
|
|
124
|
+
return resolve2(voluteSystemDir(), "backup-state.json");
|
|
125
|
+
}
|
|
126
|
+
function readBackupState() {
|
|
127
|
+
const path = stateFilePath();
|
|
128
|
+
if (!existsSync(path)) return {};
|
|
129
|
+
try {
|
|
130
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
131
|
+
} catch (err) {
|
|
132
|
+
blog.warn("backup-state.json unreadable, treating as empty", logger_default.errorData(err));
|
|
133
|
+
return {};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function writeBackupState(state) {
|
|
137
|
+
try {
|
|
138
|
+
writeFileSync2(stateFilePath(), `${JSON.stringify(state, null, 2)}
|
|
139
|
+
`);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
blog.error("failed to write backup-state.json", logger_default.errorData(err));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function stagingDir() {
|
|
145
|
+
return resolve2(voluteSystemDir(), "backup-staging");
|
|
146
|
+
}
|
|
147
|
+
async function stageDatabases() {
|
|
148
|
+
const staging = stagingDir();
|
|
149
|
+
rmSync(staging, { recursive: true, force: true });
|
|
150
|
+
mkdirSync(staging, { recursive: true });
|
|
151
|
+
const staged = [];
|
|
152
|
+
const mainTarget = resolve2(staging, "volute.db");
|
|
153
|
+
const db = await getDb();
|
|
154
|
+
await db.run(sql.raw(`VACUUM INTO '${mainTarget.replaceAll("'", "''")}'`));
|
|
155
|
+
staged.push(mainTarget);
|
|
156
|
+
const extDataDir = resolve2(voluteSystemDir(), "extension-data");
|
|
157
|
+
if (existsSync(extDataDir)) {
|
|
158
|
+
const { default: Database } = await import("libsql");
|
|
159
|
+
for (const id of readdirSync(extDataDir)) {
|
|
160
|
+
const dbPath = resolve2(extDataDir, id, "data.db");
|
|
161
|
+
if (!existsSync(dbPath)) continue;
|
|
162
|
+
const target = resolve2(staging, "extension-data", id, "data.db");
|
|
163
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
164
|
+
const extDb = new Database(dbPath);
|
|
165
|
+
try {
|
|
166
|
+
extDb.exec(`VACUUM INTO '${target.replaceAll("'", "''")}'`);
|
|
167
|
+
staged.push(target);
|
|
168
|
+
} finally {
|
|
169
|
+
extDb.close();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return staged;
|
|
174
|
+
}
|
|
175
|
+
async function backupRoots() {
|
|
176
|
+
const roots = /* @__PURE__ */ new Set([voluteHome()]);
|
|
177
|
+
const mindsDir = process.env.VOLUTE_MINDS_DIR;
|
|
178
|
+
if (mindsDir && existsSync(mindsDir)) roots.add(resolve2(mindsDir));
|
|
179
|
+
for (const mind of await readAllMinds()) {
|
|
180
|
+
if (!mind.dir || !existsSync(mind.dir)) continue;
|
|
181
|
+
const dir = resolve2(mind.dir);
|
|
182
|
+
if (![...roots].some((root) => dir === root || dir.startsWith(`${root}/`))) {
|
|
183
|
+
roots.add(dir);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return [...roots];
|
|
187
|
+
}
|
|
188
|
+
var backupInFlight = false;
|
|
189
|
+
async function runBackup() {
|
|
190
|
+
if (backupInFlight) throw new Error("A backup is already running");
|
|
191
|
+
backupInFlight = true;
|
|
192
|
+
const started = Date.now();
|
|
193
|
+
try {
|
|
194
|
+
const config = readGlobalConfig().backup ?? {};
|
|
195
|
+
if (!config.repository || !config.password) {
|
|
196
|
+
throw new Error("Backup repository is not configured. Run: volute backup init");
|
|
197
|
+
}
|
|
198
|
+
blog.info("starting backup", { repository: config.repository });
|
|
199
|
+
await stageDatabases();
|
|
200
|
+
const roots = await backupRoots();
|
|
201
|
+
const excludeFile = writeExcludeFile(config);
|
|
202
|
+
const out = await runRestic(
|
|
203
|
+
[
|
|
204
|
+
"backup",
|
|
205
|
+
...roots,
|
|
206
|
+
"--exclude-file",
|
|
207
|
+
excludeFile,
|
|
208
|
+
"--exclude-caches",
|
|
209
|
+
"--tag",
|
|
210
|
+
"volute",
|
|
211
|
+
"--json"
|
|
212
|
+
],
|
|
213
|
+
config
|
|
214
|
+
);
|
|
215
|
+
const summary = parseBackupSummary(out, Date.now() - started);
|
|
216
|
+
const keep = { ...DEFAULT_KEEP, ...config.keep };
|
|
217
|
+
let pruneError;
|
|
218
|
+
try {
|
|
219
|
+
await runRestic(
|
|
220
|
+
[
|
|
221
|
+
"forget",
|
|
222
|
+
"--tag",
|
|
223
|
+
"volute",
|
|
224
|
+
"--keep-daily",
|
|
225
|
+
String(keep.daily),
|
|
226
|
+
"--keep-weekly",
|
|
227
|
+
String(keep.weekly),
|
|
228
|
+
"--keep-monthly",
|
|
229
|
+
String(keep.monthly),
|
|
230
|
+
"--prune"
|
|
231
|
+
],
|
|
232
|
+
config
|
|
233
|
+
);
|
|
234
|
+
} catch (err) {
|
|
235
|
+
pruneError = err instanceof Error ? err.message : String(err);
|
|
236
|
+
blog.warn("retention prune failed (snapshot is intact)", logger_default.errorData(err));
|
|
237
|
+
}
|
|
238
|
+
writeBackupState({
|
|
239
|
+
lastRun: (/* @__PURE__ */ new Date()).toISOString(),
|
|
240
|
+
lastSnapshotId: summary.snapshotId,
|
|
241
|
+
lastDurationMs: summary.durationMs,
|
|
242
|
+
...pruneError ? { pruneError } : {}
|
|
243
|
+
});
|
|
244
|
+
blog.info("backup complete", { snapshot: summary.snapshotId, ms: summary.durationMs });
|
|
245
|
+
return summary;
|
|
246
|
+
} catch (err) {
|
|
247
|
+
writeBackupState({
|
|
248
|
+
...readBackupState(),
|
|
249
|
+
lastAttempt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
250
|
+
lastError: err instanceof Error ? err.message : String(err)
|
|
251
|
+
});
|
|
252
|
+
throw err;
|
|
253
|
+
} finally {
|
|
254
|
+
backupInFlight = false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function parseBackupSummary(output, durationMs) {
|
|
258
|
+
for (const line of output.trim().split("\n").reverse()) {
|
|
259
|
+
let msg;
|
|
260
|
+
try {
|
|
261
|
+
msg = JSON.parse(line);
|
|
262
|
+
} catch {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (msg.message_type !== "summary") continue;
|
|
266
|
+
if (!msg.snapshot_id) throw new Error("restic summary is missing snapshot_id");
|
|
267
|
+
return {
|
|
268
|
+
snapshotId: msg.snapshot_id,
|
|
269
|
+
filesNew: msg.files_new ?? 0,
|
|
270
|
+
filesChanged: msg.files_changed ?? 0,
|
|
271
|
+
dataAdded: msg.data_added ?? 0,
|
|
272
|
+
totalFilesProcessed: msg.total_files_processed ?? 0,
|
|
273
|
+
durationMs
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
throw new Error("restic backup produced no summary");
|
|
277
|
+
}
|
|
278
|
+
async function listSnapshots() {
|
|
279
|
+
const config = readGlobalConfig().backup ?? {};
|
|
280
|
+
const out = await runRestic(["snapshots", "--tag", "volute", "--json"], config);
|
|
281
|
+
const parsed = JSON.parse(out || "[]");
|
|
282
|
+
return parsed.map((s) => ({
|
|
283
|
+
id: s.id,
|
|
284
|
+
short_id: s.short_id,
|
|
285
|
+
time: s.time,
|
|
286
|
+
hostname: s.hostname,
|
|
287
|
+
paths: s.paths
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
290
|
+
async function initRepo(config) {
|
|
291
|
+
try {
|
|
292
|
+
await runRestic(["init"], config);
|
|
293
|
+
} catch (err) {
|
|
294
|
+
const stderr = err.stderr ?? "";
|
|
295
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
296
|
+
if (/already exists|already initialized/i.test(stderr + msg)) return;
|
|
297
|
+
throw err;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function restoreStagedDb(stagedPath, livePath) {
|
|
301
|
+
if (!existsSync(stagedPath)) return false;
|
|
302
|
+
mkdirSync(dirname(livePath), { recursive: true });
|
|
303
|
+
rmSync(livePath, { force: true });
|
|
304
|
+
rmSync(`${livePath}-wal`, { force: true });
|
|
305
|
+
rmSync(`${livePath}-shm`, { force: true });
|
|
306
|
+
renameSync(stagedPath, livePath);
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export {
|
|
311
|
+
RESTIC_INSTALL_HINT,
|
|
312
|
+
resticVersion,
|
|
313
|
+
readBackupState,
|
|
314
|
+
runBackup,
|
|
315
|
+
listSnapshots,
|
|
316
|
+
initRepo,
|
|
317
|
+
restoreStagedDb
|
|
318
|
+
};
|
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
} from "./chunk-5GT7URXJ.js";
|
|
5
5
|
import {
|
|
6
6
|
spiritDir
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-5AUKCNPE.js";
|
|
8
8
|
import {
|
|
9
9
|
hashSkillDir,
|
|
10
10
|
importSkillFromDir,
|
|
11
11
|
removeSharedSkill,
|
|
12
12
|
sharedSkillsDir
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-Y6OPHHGM.js";
|
|
14
14
|
import {
|
|
15
15
|
addMessage,
|
|
16
16
|
createChannel,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
getParticipants,
|
|
21
21
|
joinChannel,
|
|
22
22
|
publish as publish2
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-JSRMEY52.js";
|
|
24
24
|
import {
|
|
25
25
|
readVoluteConfig,
|
|
26
26
|
writeVoluteConfig
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
import {
|
|
29
29
|
isSandboxEnabled,
|
|
30
30
|
wrapForSandbox
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-OXXTPILE.js";
|
|
32
32
|
import {
|
|
33
33
|
markIdle,
|
|
34
34
|
onMindEvent
|
|
@@ -39,14 +39,14 @@ import {
|
|
|
39
39
|
resolveDeliveryMode,
|
|
40
40
|
resolveRoute,
|
|
41
41
|
setRoutesChangeListener
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-ZIR7HSAX.js";
|
|
43
43
|
import {
|
|
44
44
|
clearMind,
|
|
45
45
|
getPrompt,
|
|
46
46
|
linkInboundToActiveTurn,
|
|
47
47
|
publish as publish3,
|
|
48
48
|
summarizeOrphanedTurns
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-V4GYPD5L.js";
|
|
50
50
|
import {
|
|
51
51
|
chownMindDir,
|
|
52
52
|
exec,
|
|
@@ -58,11 +58,11 @@ import {
|
|
|
58
58
|
aiCompleteUtility,
|
|
59
59
|
getAiConfig,
|
|
60
60
|
resolveApiKey
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-TSK5PI4L.js";
|
|
62
62
|
import {
|
|
63
63
|
readGlobalConfig,
|
|
64
64
|
writeGlobalConfig
|
|
65
|
-
} from "./chunk-
|
|
65
|
+
} from "./chunk-322KE457.js";
|
|
66
66
|
import {
|
|
67
67
|
getOrCreateMindUser,
|
|
68
68
|
getOrCreateSystemUser,
|
|
@@ -385,7 +385,8 @@ async function clearDeliveredNotices(mind, session, uptoId) {
|
|
|
385
385
|
var NOTICE_HEADER = "[Notices] While you were unavailable, one or more turns failed. You're back now:";
|
|
386
386
|
function formatNotices(notices) {
|
|
387
387
|
if (notices.length === 0) return null;
|
|
388
|
-
const failures = notices.filter((n) => n.kind !== "extension");
|
|
388
|
+
const failures = notices.filter((n) => n.kind !== "extension" && n.kind !== "budget");
|
|
389
|
+
const budgets = notices.filter((n) => n.kind === "budget");
|
|
389
390
|
const extensions = notices.filter((n) => n.kind === "extension");
|
|
390
391
|
const blocks = [];
|
|
391
392
|
if (failures.length > 0) {
|
|
@@ -407,6 +408,11 @@ function formatNotices(notices) {
|
|
|
407
408
|
return `- ${g.count} ${plural} failed (${span}): ${g.detail}`;
|
|
408
409
|
});
|
|
409
410
|
blocks.push(`${NOTICE_HEADER}
|
|
411
|
+
${lines.join("\n")}`);
|
|
412
|
+
}
|
|
413
|
+
if (budgets.length > 0) {
|
|
414
|
+
const lines = budgets.map((n) => `- ${localHM(n.created_at)} ${n.detail}`);
|
|
415
|
+
blocks.push(`[Budget]
|
|
410
416
|
${lines.join("\n")}`);
|
|
411
417
|
}
|
|
412
418
|
if (extensions.length > 0) {
|
|
@@ -887,6 +893,8 @@ var MindManager = class {
|
|
|
887
893
|
const parts = [];
|
|
888
894
|
if (context.type === "merge" || context.type === "merged") {
|
|
889
895
|
parts.push(await getPrompt("merge_message", { name: String(context.name ?? "") }));
|
|
896
|
+
} else if (context.type === "split") {
|
|
897
|
+
parts.push(await getPrompt("split_message", { name, parent: String(context.parent ?? "") }));
|
|
890
898
|
} else if (context.type === "sprouted") {
|
|
891
899
|
parts.push(await getPrompt("sprout_message"));
|
|
892
900
|
} else if (context.type === "upgraded") {
|
|
@@ -931,7 +939,7 @@ var MindManager = class {
|
|
|
931
939
|
if (this.shuttingDown || this.stopping.has(name)) return;
|
|
932
940
|
mlog.error(`mind ${name} exited with code ${code}`);
|
|
933
941
|
try {
|
|
934
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
942
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-YAHDOIU5.js");
|
|
935
943
|
const sleepMgr = getSleepManagerIfReady2();
|
|
936
944
|
const sleepState = sleepMgr?.getState(name);
|
|
937
945
|
if (sleepState?.sleeping) {
|
|
@@ -961,7 +969,7 @@ var MindManager = class {
|
|
|
961
969
|
(err) => mlog.warn(`failed to clear turn state for ${name} after crash`, logger_default.errorData(err))
|
|
962
970
|
);
|
|
963
971
|
try {
|
|
964
|
-
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-
|
|
972
|
+
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-R5STGLXB.js");
|
|
965
973
|
getDeliveryManager2().clearMindSessions(name);
|
|
966
974
|
} catch (err) {
|
|
967
975
|
if (!(err instanceof Error && err.message.includes("not initialized"))) {
|
|
@@ -1027,7 +1035,7 @@ var MindManager = class {
|
|
|
1027
1035
|
mlog.warn(`failed to clear turn state for ${name} on stop`, logger_default.errorData(err));
|
|
1028
1036
|
}
|
|
1029
1037
|
try {
|
|
1030
|
-
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-
|
|
1038
|
+
const { getDeliveryManager: getDeliveryManager2 } = await import("./delivery-manager-R5STGLXB.js");
|
|
1031
1039
|
getDeliveryManager2().clearMindSessions(name);
|
|
1032
1040
|
} catch (err) {
|
|
1033
1041
|
if (!(err instanceof Error && err.message.includes("not initialized"))) {
|
|
@@ -1035,7 +1043,7 @@ var MindManager = class {
|
|
|
1035
1043
|
}
|
|
1036
1044
|
}
|
|
1037
1045
|
try {
|
|
1038
|
-
const { clearEchoTextCache } = await import("./echo-text-
|
|
1046
|
+
const { clearEchoTextCache } = await import("./echo-text-NQXYYYCU.js");
|
|
1039
1047
|
clearEchoTextCache(name);
|
|
1040
1048
|
} catch (err) {
|
|
1041
1049
|
mlog.debug(`failed to clear echo-text cache for ${name}`, logger_default.errorData(err));
|
|
@@ -2763,7 +2771,7 @@ async function wakeMind(name) {
|
|
|
2763
2771
|
async function startSpiritFull(name) {
|
|
2764
2772
|
const entry = await findMind(name);
|
|
2765
2773
|
if (entry?.dir) {
|
|
2766
|
-
const { registerMindDir } = await import("./delivery-router-
|
|
2774
|
+
const { registerMindDir } = await import("./delivery-router-6DE3BPHO.js");
|
|
2767
2775
|
registerMindDir(name, entry.dir);
|
|
2768
2776
|
}
|
|
2769
2777
|
await getMindManager().startMind(name);
|
|
@@ -2786,7 +2794,7 @@ async function stopSpiritFull(name) {
|
|
|
2786
2794
|
}
|
|
2787
2795
|
async function ensureCreatorDM(mindName, creatorUsername) {
|
|
2788
2796
|
const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-FRZJVQCS.js");
|
|
2789
|
-
const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-
|
|
2797
|
+
const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-G4LYMHAA.js");
|
|
2790
2798
|
const mindUser = await getOrCreateMindUser2(mindName);
|
|
2791
2799
|
const creatorUser = await getUserByUsername2(creatorUsername);
|
|
2792
2800
|
if (!creatorUser) {
|
|
@@ -2838,6 +2846,22 @@ function formatCurrentDate() {
|
|
|
2838
2846
|
day: "numeric"
|
|
2839
2847
|
});
|
|
2840
2848
|
}
|
|
2849
|
+
function formatWakeTime(wakeAt) {
|
|
2850
|
+
if (wakeAt) {
|
|
2851
|
+
const date = new Date(wakeAt);
|
|
2852
|
+
if (!Number.isNaN(date.getTime())) {
|
|
2853
|
+
const time = date.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" });
|
|
2854
|
+
const day = date.toLocaleDateString("en-US", {
|
|
2855
|
+
weekday: "long",
|
|
2856
|
+
month: "long",
|
|
2857
|
+
day: "numeric"
|
|
2858
|
+
});
|
|
2859
|
+
return `at ${time} on ${day}`;
|
|
2860
|
+
}
|
|
2861
|
+
return `at ${wakeAt}`;
|
|
2862
|
+
}
|
|
2863
|
+
return "when something needs you \u2014 a message, a mention, or someone waking you";
|
|
2864
|
+
}
|
|
2841
2865
|
function formatDuration(from, to) {
|
|
2842
2866
|
const ms = to.getTime() - from.getTime();
|
|
2843
2867
|
const hours = Math.floor(ms / 36e5);
|
|
@@ -2985,7 +3009,8 @@ var SleepManager = class {
|
|
|
2985
3009
|
const entry = await findMind(name);
|
|
2986
3010
|
if (!entry) return;
|
|
2987
3011
|
const sleepConfig = this.getSleepConfig(name);
|
|
2988
|
-
const
|
|
3012
|
+
const wakeAt = opts?.voluntaryWakeAt ?? this.getNextWakeTime(sleepConfig);
|
|
3013
|
+
const wakeTime = formatWakeTime(wakeAt);
|
|
2989
3014
|
const preSleepMsg = await getPrompt("pre_sleep", { wakeTime });
|
|
2990
3015
|
let conversationId;
|
|
2991
3016
|
try {
|
|
@@ -3161,7 +3186,7 @@ var SleepManager = class {
|
|
|
3161
3186
|
const db = await getDb();
|
|
3162
3187
|
const rows = await db.select().from(deliveryQueue).where(and2(eq3(deliveryQueue.mind, name), eq3(deliveryQueue.status, "sleep-queued"))).all();
|
|
3163
3188
|
if (rows.length === 0) return 0;
|
|
3164
|
-
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-
|
|
3189
|
+
const { deliverMessage: deliverMessage2 } = await import("./message-delivery-IUAXRPFE.js");
|
|
3165
3190
|
const delivered = [];
|
|
3166
3191
|
for (const row of rows) {
|
|
3167
3192
|
try {
|
|
@@ -4308,16 +4333,17 @@ var DeliveryManager = class {
|
|
|
4308
4333
|
const channel = payload.channel ?? "unknown";
|
|
4309
4334
|
const notification = [
|
|
4310
4335
|
`[New channel: ${channel}]`,
|
|
4336
|
+
`Someone new is reaching out \u2014 you don't have a route for this channel yet.`,
|
|
4311
4337
|
`Sender: ${payload.sender ?? "unknown"}`,
|
|
4312
4338
|
payload.platform ? `Platform: ${payload.platform}` : null,
|
|
4313
4339
|
payload.participantCount ? `Participants: ${payload.participantCount}` : null,
|
|
4314
4340
|
"",
|
|
4315
4341
|
`Preview: ${preview}`,
|
|
4316
4342
|
"",
|
|
4317
|
-
`To
|
|
4318
|
-
`Messages are
|
|
4343
|
+
`To start hearing this channel, add a routing rule for "${channel}" to .config/routes.json.`,
|
|
4344
|
+
`Messages are held safely until you do \u2014 nothing is lost. If you'd rather not engage, just leave it unrouted.`
|
|
4319
4345
|
].filter((line) => line !== null).join("\n");
|
|
4320
|
-
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-
|
|
4346
|
+
const { sendSystemMessage: sendSystemMessage2 } = await import("./system-chat-E65T2HSW.js");
|
|
4321
4347
|
await sendSystemMessage2(mindName, notification);
|
|
4322
4348
|
}
|
|
4323
4349
|
/**
|
|
@@ -4758,7 +4784,7 @@ async function generateSystemReply(conversationId, mindName, message) {
|
|
|
4758
4784
|
if (config.sleep.schedule?.wake) contextParts.push(`Wake cron: ${config.sleep.schedule.wake}`);
|
|
4759
4785
|
}
|
|
4760
4786
|
try {
|
|
4761
|
-
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-
|
|
4787
|
+
const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-YAHDOIU5.js");
|
|
4762
4788
|
const sm = getSleepManagerIfReady2();
|
|
4763
4789
|
if (sm) {
|
|
4764
4790
|
const state2 = sm.getState(mindName);
|