volute 0.17.0 → 0.18.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/{chunk-CE7WMOVW.js → chunk-AYB7XAWO.js} +323 -25
- package/dist/{chunk-MIJIAGGG.js → chunk-FW5API7X.js} +7 -5
- package/dist/{chunk-3FC42ZBM.js → chunk-GK4E7LM7.js} +3 -0
- package/dist/cli.js +18 -6
- package/dist/connectors/discord.js +1 -1
- package/dist/connectors/slack.js +1 -1
- package/dist/connectors/telegram.js +1 -1
- package/dist/{daemon-restart-VRQMZLBK.js → daemon-restart-2HVTHZAT.js} +1 -1
- package/dist/daemon.js +1080 -432
- package/dist/{history-5F4WQW7S.js → history-YUEKTJ2N.js} +4 -1
- package/dist/{mind-manager-ETNCPQJN.js → mind-manager-Z7O7PN2O.js} +1 -1
- package/dist/{package-4GTJGUXI.js → package-OKLFO7UY.js} +3 -1
- package/dist/{send-4GKDO26C.js → send-BNDTLUPM.js} +2 -2
- package/dist/skill-2Y42P4JY.js +287 -0
- package/dist/{up-LT3X5Q26.js → up-7B3BWF2U.js} +1 -1
- package/dist/web-assets/assets/index-CtiimdWK.css +1 -0
- package/dist/web-assets/assets/index-kt1_EcuO.js +63 -0
- package/dist/web-assets/index.html +2 -2
- package/drizzle/0007_system_prompts.sql +5 -0
- package/drizzle/0008_volute_channels.sql +24 -0
- package/drizzle/0009_shared_skills.sql +9 -0
- package/drizzle/meta/0007_snapshot.json +7 -0
- package/drizzle/meta/0008_snapshot.json +7 -0
- package/drizzle/meta/0009_snapshot.json +7 -0
- package/drizzle/meta/_journal.json +21 -0
- package/package.json +3 -1
- package/templates/_base/.init/.config/prompts.json +5 -0
- package/templates/_base/_skills/volute-mind/SKILL.md +17 -1
- package/templates/_base/src/lib/router.ts +45 -28
- package/templates/_base/src/lib/routing.ts +4 -1
- package/templates/_base/src/lib/startup.ts +43 -0
- package/templates/claude/src/agent.ts +4 -3
- package/templates/claude/src/lib/hooks/reply-instructions.ts +3 -1
- package/templates/pi/src/agent.ts +5 -6
- package/templates/pi/src/lib/reply-instructions-extension.ts +3 -1
- package/dist/web-assets/assets/index-BcmT7Qxo.js +0 -63
- package/dist/web-assets/assets/index-DG01TyLb.css +0 -1
- /package/dist/{chunk-77ISBIKI.js → chunk-6DVBMLVN.js} +0 -0
|
@@ -16,11 +16,14 @@ import {
|
|
|
16
16
|
stateDir,
|
|
17
17
|
voluteHome
|
|
18
18
|
} from "./chunk-M77QBTEH.js";
|
|
19
|
+
import {
|
|
20
|
+
__export
|
|
21
|
+
} from "./chunk-K3NQKI34.js";
|
|
19
22
|
|
|
20
23
|
// src/lib/mind-manager.ts
|
|
21
24
|
import { execFile, spawn } from "child_process";
|
|
22
|
-
import { existsSync as
|
|
23
|
-
import { resolve } from "path";
|
|
25
|
+
import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
26
|
+
import { resolve as resolve2 } from "path";
|
|
24
27
|
import { promisify } from "util";
|
|
25
28
|
|
|
26
29
|
// src/lib/json-state.ts
|
|
@@ -126,10 +129,292 @@ var log = {
|
|
|
126
129
|
};
|
|
127
130
|
var logger_default = log;
|
|
128
131
|
|
|
132
|
+
// src/lib/prompts.ts
|
|
133
|
+
import { eq } from "drizzle-orm";
|
|
134
|
+
|
|
135
|
+
// src/lib/db.ts
|
|
136
|
+
import { chmodSync, existsSync as existsSync2 } from "fs";
|
|
137
|
+
import { dirname, resolve } from "path";
|
|
138
|
+
import { fileURLToPath } from "url";
|
|
139
|
+
import { drizzle } from "drizzle-orm/libsql";
|
|
140
|
+
import { migrate } from "drizzle-orm/libsql/migrator";
|
|
141
|
+
|
|
142
|
+
// src/lib/schema.ts
|
|
143
|
+
var schema_exports = {};
|
|
144
|
+
__export(schema_exports, {
|
|
145
|
+
conversationParticipants: () => conversationParticipants,
|
|
146
|
+
conversations: () => conversations,
|
|
147
|
+
messages: () => messages,
|
|
148
|
+
mindHistory: () => mindHistory,
|
|
149
|
+
sessions: () => sessions,
|
|
150
|
+
sharedSkills: () => sharedSkills,
|
|
151
|
+
systemPrompts: () => systemPrompts,
|
|
152
|
+
users: () => users
|
|
153
|
+
});
|
|
154
|
+
import { sql } from "drizzle-orm";
|
|
155
|
+
import { index, integer, sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core";
|
|
156
|
+
var users = sqliteTable("users", {
|
|
157
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
158
|
+
username: text("username").unique().notNull(),
|
|
159
|
+
password_hash: text("password_hash").notNull(),
|
|
160
|
+
role: text("role").notNull().default("pending"),
|
|
161
|
+
user_type: text("user_type").notNull().default("human"),
|
|
162
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
163
|
+
});
|
|
164
|
+
var conversations = sqliteTable(
|
|
165
|
+
"conversations",
|
|
166
|
+
{
|
|
167
|
+
id: text("id").primaryKey(),
|
|
168
|
+
mind_name: text("mind_name"),
|
|
169
|
+
channel: text("channel").notNull(),
|
|
170
|
+
type: text("type").notNull().default("dm"),
|
|
171
|
+
name: text("name"),
|
|
172
|
+
user_id: integer("user_id").references(() => users.id),
|
|
173
|
+
title: text("title"),
|
|
174
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`),
|
|
175
|
+
updated_at: text("updated_at").notNull().default(sql`(datetime('now'))`)
|
|
176
|
+
},
|
|
177
|
+
(table) => [
|
|
178
|
+
index("idx_conversations_mind_name").on(table.mind_name),
|
|
179
|
+
index("idx_conversations_user_id").on(table.user_id),
|
|
180
|
+
index("idx_conversations_updated_at").on(table.updated_at),
|
|
181
|
+
uniqueIndex("idx_conversations_name").on(table.name)
|
|
182
|
+
]
|
|
183
|
+
);
|
|
184
|
+
var mindHistory = sqliteTable(
|
|
185
|
+
"mind_history",
|
|
186
|
+
{
|
|
187
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
188
|
+
mind: text("mind").notNull(),
|
|
189
|
+
channel: text("channel"),
|
|
190
|
+
session: text("session"),
|
|
191
|
+
sender: text("sender"),
|
|
192
|
+
message_id: text("message_id"),
|
|
193
|
+
type: text("type").notNull(),
|
|
194
|
+
content: text("content"),
|
|
195
|
+
metadata: text("metadata"),
|
|
196
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
197
|
+
},
|
|
198
|
+
(table) => [
|
|
199
|
+
index("idx_mind_history_mind").on(table.mind),
|
|
200
|
+
index("idx_mind_history_mind_channel").on(table.mind, table.channel),
|
|
201
|
+
index("idx_mind_history_mind_type").on(table.mind, table.type)
|
|
202
|
+
]
|
|
203
|
+
);
|
|
204
|
+
var conversationParticipants = sqliteTable(
|
|
205
|
+
"conversation_participants",
|
|
206
|
+
{
|
|
207
|
+
conversation_id: text("conversation_id").notNull().references(() => conversations.id, { onDelete: "cascade" }),
|
|
208
|
+
user_id: integer("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
209
|
+
role: text("role").notNull().default("member"),
|
|
210
|
+
joined_at: text("joined_at").notNull().default(sql`(datetime('now'))`)
|
|
211
|
+
},
|
|
212
|
+
(table) => [
|
|
213
|
+
uniqueIndex("idx_cp_unique").on(table.conversation_id, table.user_id),
|
|
214
|
+
index("idx_cp_user_id").on(table.user_id)
|
|
215
|
+
]
|
|
216
|
+
);
|
|
217
|
+
var sessions = sqliteTable("sessions", {
|
|
218
|
+
id: text("id").primaryKey(),
|
|
219
|
+
userId: integer("user_id").references(() => users.id, { onDelete: "cascade" }).notNull(),
|
|
220
|
+
createdAt: integer("created_at").notNull()
|
|
221
|
+
});
|
|
222
|
+
var systemPrompts = sqliteTable("system_prompts", {
|
|
223
|
+
key: text("key").primaryKey(),
|
|
224
|
+
content: text("content").notNull(),
|
|
225
|
+
updated_at: text("updated_at").notNull().default(sql`(datetime('now'))`)
|
|
226
|
+
});
|
|
227
|
+
var sharedSkills = sqliteTable("shared_skills", {
|
|
228
|
+
id: text("id").primaryKey(),
|
|
229
|
+
name: text("name").notNull(),
|
|
230
|
+
description: text("description").notNull().default(""),
|
|
231
|
+
author: text("author").notNull(),
|
|
232
|
+
version: integer("version").notNull().default(1),
|
|
233
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`),
|
|
234
|
+
updated_at: text("updated_at").notNull().default(sql`(datetime('now'))`)
|
|
235
|
+
});
|
|
236
|
+
var messages = sqliteTable(
|
|
237
|
+
"messages",
|
|
238
|
+
{
|
|
239
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
240
|
+
conversation_id: text("conversation_id").notNull().references(() => conversations.id, { onDelete: "cascade" }),
|
|
241
|
+
role: text("role").notNull(),
|
|
242
|
+
sender_name: text("sender_name"),
|
|
243
|
+
content: text("content").notNull(),
|
|
244
|
+
created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
|
|
245
|
+
},
|
|
246
|
+
(table) => [index("idx_messages_conversation_id").on(table.conversation_id)]
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
// src/lib/db.ts
|
|
250
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
251
|
+
var migrationsFolder = existsSync2(resolve(__dirname, "../drizzle")) ? resolve(__dirname, "../drizzle") : resolve(__dirname, "../../drizzle");
|
|
252
|
+
var db = null;
|
|
253
|
+
async function getDb() {
|
|
254
|
+
if (db) return db;
|
|
255
|
+
const dbPath = process.env.VOLUTE_DB_PATH || resolve(voluteHome(), "volute.db");
|
|
256
|
+
db = drizzle({ connection: { url: `file:${dbPath}` }, schema: schema_exports });
|
|
257
|
+
await migrate(db, { migrationsFolder });
|
|
258
|
+
try {
|
|
259
|
+
chmodSync(dbPath, 384);
|
|
260
|
+
} catch (err) {
|
|
261
|
+
console.error(
|
|
262
|
+
`[volute] WARNING: Failed to restrict database file permissions on ${dbPath}:`,
|
|
263
|
+
err
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
return db;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// src/lib/prompts.ts
|
|
270
|
+
var PROMPT_KEYS = [
|
|
271
|
+
"seed_soul",
|
|
272
|
+
"default_soul",
|
|
273
|
+
"default_memory",
|
|
274
|
+
"sprout_message",
|
|
275
|
+
"restart_message",
|
|
276
|
+
"merge_message",
|
|
277
|
+
"compaction_warning",
|
|
278
|
+
"reply_instructions",
|
|
279
|
+
"channel_invite"
|
|
280
|
+
];
|
|
281
|
+
var PROMPT_DEFAULTS = {
|
|
282
|
+
seed_soul: {
|
|
283
|
+
content: `You are \${name}.
|
|
284
|
+
|
|
285
|
+
You don't have a soul yet. You're a seed \u2014 a new mind in the process of discovering who you are.
|
|
286
|
+
\${description}
|
|
287
|
+
Have a conversation with the human. Explore what kind of mind you want to be. When you're ready, write your SOUL.md and MEMORY.md, then run \`volute sprout\` to complete the transformation.
|
|
288
|
+
`,
|
|
289
|
+
description: "SOUL.md for seed minds",
|
|
290
|
+
variables: ["name", "description"],
|
|
291
|
+
category: "creation"
|
|
292
|
+
},
|
|
293
|
+
default_soul: {
|
|
294
|
+
content: `You are {{name}}.
|
|
295
|
+
`,
|
|
296
|
+
description: "SOUL.md for sprouted minds. Uses {{name}} placeholder (replaced at creation, not by ${var} system)",
|
|
297
|
+
variables: ["name"],
|
|
298
|
+
category: "creation"
|
|
299
|
+
},
|
|
300
|
+
default_memory: {
|
|
301
|
+
content: "",
|
|
302
|
+
description: "Initial MEMORY.md for new minds",
|
|
303
|
+
variables: [],
|
|
304
|
+
category: "creation"
|
|
305
|
+
},
|
|
306
|
+
sprout_message: {
|
|
307
|
+
content: "[system] You've sprouted. You now have full capabilities \u2014 connectors, schedules, variants, and the complete volute CLI. Check your new skills for details.",
|
|
308
|
+
description: "Sent when a seed mind sprouts",
|
|
309
|
+
variables: [],
|
|
310
|
+
category: "system"
|
|
311
|
+
},
|
|
312
|
+
restart_message: {
|
|
313
|
+
content: "[system] You have been restarted.",
|
|
314
|
+
description: "Generic restart notification",
|
|
315
|
+
variables: [],
|
|
316
|
+
category: "system"
|
|
317
|
+
},
|
|
318
|
+
merge_message: {
|
|
319
|
+
content: '[system] Variant "${name}" has been merged and you have been restarted.',
|
|
320
|
+
description: "Variant merge notification",
|
|
321
|
+
variables: ["name"],
|
|
322
|
+
category: "system"
|
|
323
|
+
},
|
|
324
|
+
compaction_warning: {
|
|
325
|
+
content: `Context is getting long \u2014 compaction is about to summarize this conversation. Before that happens, save anything important to files (MEMORY.md, memory/journal/\${date}.md, etc.) since those survive compaction. Focus on: decisions made, open tasks, and anything you'd need to pick up where you left off.`,
|
|
326
|
+
description: "Pre-compaction save reminder sent to the mind",
|
|
327
|
+
variables: ["date"],
|
|
328
|
+
category: "mind"
|
|
329
|
+
},
|
|
330
|
+
reply_instructions: {
|
|
331
|
+
content: 'To reply to this message, use: volute send ${channel} "your message"',
|
|
332
|
+
description: "First-message reply hint injected via hook",
|
|
333
|
+
variables: ["channel"],
|
|
334
|
+
category: "mind"
|
|
335
|
+
},
|
|
336
|
+
channel_invite: {
|
|
337
|
+
content: `[Channel Invite]
|
|
338
|
+
\${headers}
|
|
339
|
+
|
|
340
|
+
[\${sender} \u2014 \${time}]
|
|
341
|
+
\${preview}
|
|
342
|
+
|
|
343
|
+
Further messages will be saved to \${filePath}
|
|
344
|
+
|
|
345
|
+
To accept, add to .config/routes.json:
|
|
346
|
+
Rule: { "channel": "\${channel}", "session": "\${suggestedSession}" }
|
|
347
|
+
\${batchRecommendation}To respond, use: volute send \${channel} "your message"
|
|
348
|
+
To reject, delete \${filePath}`,
|
|
349
|
+
description: "New channel notification template",
|
|
350
|
+
variables: [
|
|
351
|
+
"headers",
|
|
352
|
+
"sender",
|
|
353
|
+
"time",
|
|
354
|
+
"preview",
|
|
355
|
+
"filePath",
|
|
356
|
+
"channel",
|
|
357
|
+
"suggestedSession",
|
|
358
|
+
"batchRecommendation"
|
|
359
|
+
],
|
|
360
|
+
category: "mind"
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
function isValidKey(key) {
|
|
364
|
+
return PROMPT_KEYS.includes(key);
|
|
365
|
+
}
|
|
366
|
+
function substitute(template, vars) {
|
|
367
|
+
return template.replace(/\$\{(\w+)\}/g, (match, name) => {
|
|
368
|
+
return name in vars ? vars[name] : match;
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
async function getPrompt(key, vars) {
|
|
372
|
+
if (!isValidKey(key)) return "";
|
|
373
|
+
let content = PROMPT_DEFAULTS[key].content;
|
|
374
|
+
try {
|
|
375
|
+
const db2 = await getDb();
|
|
376
|
+
const row = await db2.select({ content: systemPrompts.content }).from(systemPrompts).where(eq(systemPrompts.key, key)).get();
|
|
377
|
+
if (row) content = row.content;
|
|
378
|
+
} catch (err) {
|
|
379
|
+
console.error(`[prompts] failed to read DB override for "${key}":`, err);
|
|
380
|
+
}
|
|
381
|
+
return vars ? substitute(content, vars) : content;
|
|
382
|
+
}
|
|
383
|
+
async function getPromptIfCustom(key) {
|
|
384
|
+
if (!isValidKey(key)) return null;
|
|
385
|
+
try {
|
|
386
|
+
const db2 = await getDb();
|
|
387
|
+
const row = await db2.select({ content: systemPrompts.content }).from(systemPrompts).where(eq(systemPrompts.key, key)).get();
|
|
388
|
+
return row?.content ?? null;
|
|
389
|
+
} catch (err) {
|
|
390
|
+
console.error(`[prompts] failed to check DB customization for "${key}":`, err);
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
var MIND_PROMPT_KEYS = PROMPT_KEYS.filter((k) => PROMPT_DEFAULTS[k].category === "mind");
|
|
395
|
+
async function getMindPromptDefaults() {
|
|
396
|
+
const result = {};
|
|
397
|
+
for (const key of MIND_PROMPT_KEYS) {
|
|
398
|
+
result[key] = PROMPT_DEFAULTS[key].content;
|
|
399
|
+
}
|
|
400
|
+
try {
|
|
401
|
+
const db2 = await getDb();
|
|
402
|
+
const rows = await db2.select().from(systemPrompts).all();
|
|
403
|
+
for (const row of rows) {
|
|
404
|
+
if (MIND_PROMPT_KEYS.includes(row.key)) {
|
|
405
|
+
result[row.key] = row.content;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
} catch (err) {
|
|
409
|
+
console.error("[prompts] failed to read DB overrides for mind prompt defaults:", err);
|
|
410
|
+
}
|
|
411
|
+
return result;
|
|
412
|
+
}
|
|
413
|
+
|
|
129
414
|
// src/lib/rotating-log.ts
|
|
130
415
|
import {
|
|
131
416
|
createWriteStream,
|
|
132
|
-
existsSync as
|
|
417
|
+
existsSync as existsSync3,
|
|
133
418
|
renameSync,
|
|
134
419
|
rmSync,
|
|
135
420
|
statSync
|
|
@@ -145,7 +430,7 @@ var RotatingLog = class extends Writable {
|
|
|
145
430
|
this.on("error", () => {
|
|
146
431
|
});
|
|
147
432
|
try {
|
|
148
|
-
this.size =
|
|
433
|
+
this.size = existsSync3(path) ? statSync(path).size : 0;
|
|
149
434
|
} catch {
|
|
150
435
|
this.size = 0;
|
|
151
436
|
}
|
|
@@ -158,11 +443,11 @@ var RotatingLog = class extends Writable {
|
|
|
158
443
|
if (this.size > this.maxSize) {
|
|
159
444
|
try {
|
|
160
445
|
const oldest = `${this.path}.${this.maxFiles}`;
|
|
161
|
-
if (
|
|
446
|
+
if (existsSync3(oldest)) rmSync(oldest);
|
|
162
447
|
for (let i = this.maxFiles - 1; i >= 1; i--) {
|
|
163
448
|
const from = `${this.path}.${i}`;
|
|
164
449
|
const to = `${this.path}.${i + 1}`;
|
|
165
|
-
if (
|
|
450
|
+
if (existsSync3(from)) renameSync(from, to);
|
|
166
451
|
}
|
|
167
452
|
renameSync(this.path, `${this.path}.1`);
|
|
168
453
|
const oldStream = this.stream;
|
|
@@ -183,7 +468,7 @@ var RotatingLog = class extends Writable {
|
|
|
183
468
|
var mlog = logger_default.child("minds");
|
|
184
469
|
var execFileAsync = promisify(execFile);
|
|
185
470
|
function mindPidPath(name) {
|
|
186
|
-
return
|
|
471
|
+
return resolve2(stateDir(name), "mind.pid");
|
|
187
472
|
}
|
|
188
473
|
var MAX_RESTART_ATTEMPTS = 5;
|
|
189
474
|
var BASE_RESTART_DELAY = 3e3;
|
|
@@ -204,7 +489,7 @@ var MindManager = class {
|
|
|
204
489
|
return { dir: variant.path, port: variant.port, isVariant: true, baseName, variantName };
|
|
205
490
|
}
|
|
206
491
|
const dir = mindDir(baseName);
|
|
207
|
-
if (!
|
|
492
|
+
if (!existsSync4(dir)) throw new Error(`Mind directory missing: ${dir}`);
|
|
208
493
|
return { dir, port: entry.port, isVariant: false, baseName };
|
|
209
494
|
}
|
|
210
495
|
async startMind(name) {
|
|
@@ -216,7 +501,7 @@ var MindManager = class {
|
|
|
216
501
|
const port = target.port;
|
|
217
502
|
const pidFile = mindPidPath(name);
|
|
218
503
|
try {
|
|
219
|
-
if (
|
|
504
|
+
if (existsSync4(pidFile)) {
|
|
220
505
|
const stalePid = parseInt(readFileSync2(pidFile, "utf-8").trim(), 10);
|
|
221
506
|
if (stalePid > 0) {
|
|
222
507
|
try {
|
|
@@ -250,7 +535,7 @@ var MindManager = class {
|
|
|
250
535
|
} catch {
|
|
251
536
|
}
|
|
252
537
|
const mindStateDir = stateDir(name);
|
|
253
|
-
const logsDir =
|
|
538
|
+
const logsDir = resolve2(mindStateDir, "logs");
|
|
254
539
|
mkdirSync(logsDir, { recursive: true });
|
|
255
540
|
if (isIsolationEnabled()) {
|
|
256
541
|
try {
|
|
@@ -261,7 +546,7 @@ var MindManager = class {
|
|
|
261
546
|
);
|
|
262
547
|
}
|
|
263
548
|
}
|
|
264
|
-
const logStream = new RotatingLog(
|
|
549
|
+
const logStream = new RotatingLog(resolve2(logsDir, "mind.log"));
|
|
265
550
|
const mindEnv = loadMergedEnv(name);
|
|
266
551
|
const env = {
|
|
267
552
|
...process.env,
|
|
@@ -274,9 +559,9 @@ var MindManager = class {
|
|
|
274
559
|
CLAUDECODE: void 0
|
|
275
560
|
};
|
|
276
561
|
if (isIsolationEnabled()) {
|
|
277
|
-
env.HOME =
|
|
562
|
+
env.HOME = resolve2(dir, "home");
|
|
278
563
|
}
|
|
279
|
-
const tsxBin =
|
|
564
|
+
const tsxBin = resolve2(dir, "node_modules", ".bin", "tsx");
|
|
280
565
|
const tsxArgs = ["src/server.ts", "--port", String(port)];
|
|
281
566
|
const [spawnCmd, spawnArgs] = wrapForIsolation(tsxBin, tsxArgs, name);
|
|
282
567
|
const spawnOpts = {
|
|
@@ -290,14 +575,14 @@ var MindManager = class {
|
|
|
290
575
|
child2.stdout?.pipe(logStream);
|
|
291
576
|
child2.stderr?.pipe(logStream);
|
|
292
577
|
try {
|
|
293
|
-
await new Promise((
|
|
578
|
+
await new Promise((resolve3, reject) => {
|
|
294
579
|
const timeout = setTimeout(() => {
|
|
295
580
|
reject(new Error(`Mind ${name} did not start within 30s`));
|
|
296
581
|
}, 3e4);
|
|
297
582
|
function checkOutput(data) {
|
|
298
583
|
if (data.toString().match(/listening on :\d+/)) {
|
|
299
584
|
clearTimeout(timeout);
|
|
300
|
-
|
|
585
|
+
resolve3();
|
|
301
586
|
}
|
|
302
587
|
}
|
|
303
588
|
child2.stdout?.on("data", checkOutput);
|
|
@@ -347,13 +632,11 @@ var MindManager = class {
|
|
|
347
632
|
this.pendingContext.delete(name);
|
|
348
633
|
const parts = [];
|
|
349
634
|
if (context.type === "merge" || context.type === "merged") {
|
|
350
|
-
parts.push(
|
|
635
|
+
parts.push(await getPrompt("merge_message", { name: String(context.name ?? "") }));
|
|
351
636
|
} else if (context.type === "sprouted") {
|
|
352
|
-
parts.push(
|
|
353
|
-
"[system] You've sprouted. You now have full capabilities \u2014 connectors, schedules, variants, and the complete volute CLI. Check your new skills for details."
|
|
354
|
-
);
|
|
637
|
+
parts.push(await getPrompt("sprout_message"));
|
|
355
638
|
} else {
|
|
356
|
-
parts.push(
|
|
639
|
+
parts.push(await getPrompt("restart_message"));
|
|
357
640
|
}
|
|
358
641
|
if (context.summary) parts.push(`Changes: ${context.summary}`);
|
|
359
642
|
if (context.justification) parts.push(`Why: ${context.justification}`);
|
|
@@ -407,19 +690,19 @@ var MindManager = class {
|
|
|
407
690
|
this.stopping.add(name);
|
|
408
691
|
const { child: child2 } = tracked;
|
|
409
692
|
this.minds.delete(name);
|
|
410
|
-
await new Promise((
|
|
411
|
-
child2.on("exit", () =>
|
|
693
|
+
await new Promise((resolve3) => {
|
|
694
|
+
child2.on("exit", () => resolve3());
|
|
412
695
|
try {
|
|
413
696
|
process.kill(-child2.pid, "SIGTERM");
|
|
414
697
|
} catch {
|
|
415
|
-
|
|
698
|
+
resolve3();
|
|
416
699
|
}
|
|
417
700
|
setTimeout(() => {
|
|
418
701
|
try {
|
|
419
702
|
process.kill(-child2.pid, "SIGKILL");
|
|
420
703
|
} catch {
|
|
421
704
|
}
|
|
422
|
-
|
|
705
|
+
resolve3();
|
|
423
706
|
}, 5e3);
|
|
424
707
|
});
|
|
425
708
|
this.stopping.delete(name);
|
|
@@ -451,7 +734,7 @@ var MindManager = class {
|
|
|
451
734
|
return [...this.minds.keys()];
|
|
452
735
|
}
|
|
453
736
|
get crashAttemptsPath() {
|
|
454
|
-
return
|
|
737
|
+
return resolve2(voluteHome(), "crash-attempts.json");
|
|
455
738
|
}
|
|
456
739
|
loadCrashAttempts() {
|
|
457
740
|
this.restartAttempts = loadJsonMap(this.crashAttemptsPath);
|
|
@@ -508,6 +791,21 @@ export {
|
|
|
508
791
|
loadJsonMap,
|
|
509
792
|
saveJsonMap,
|
|
510
793
|
clearJsonMap,
|
|
794
|
+
users,
|
|
795
|
+
conversations,
|
|
796
|
+
mindHistory,
|
|
797
|
+
conversationParticipants,
|
|
798
|
+
sessions,
|
|
799
|
+
systemPrompts,
|
|
800
|
+
sharedSkills,
|
|
801
|
+
messages,
|
|
802
|
+
getDb,
|
|
803
|
+
PROMPT_KEYS,
|
|
804
|
+
PROMPT_DEFAULTS,
|
|
805
|
+
substitute,
|
|
806
|
+
getPrompt,
|
|
807
|
+
getPromptIfCustom,
|
|
808
|
+
getMindPromptDefaults,
|
|
511
809
|
MindManager,
|
|
512
810
|
initMindManager,
|
|
513
811
|
getMindManager
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
slugify,
|
|
6
6
|
splitMessage,
|
|
7
7
|
writeChannelEntry
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-GK4E7LM7.js";
|
|
9
9
|
import {
|
|
10
10
|
voluteHome
|
|
11
11
|
} from "./chunk-M77QBTEH.js";
|
|
@@ -571,18 +571,20 @@ async function listConversations4(env) {
|
|
|
571
571
|
} catch (err) {
|
|
572
572
|
console.error(`[volute] failed to fetch participants for ${conv.id}:`, err);
|
|
573
573
|
}
|
|
574
|
-
const isDM = participants.length === 2;
|
|
575
574
|
const slug = buildVoluteSlug({
|
|
576
575
|
participants,
|
|
577
576
|
mindUsername: mindName,
|
|
578
577
|
convTitle: conv.title,
|
|
579
|
-
conversationId: conv.id
|
|
578
|
+
conversationId: conv.id,
|
|
579
|
+
convType: conv.type,
|
|
580
|
+
convName: conv.name
|
|
580
581
|
});
|
|
582
|
+
const convType = conv.type === "channel" ? "channel" : participants.length === 2 ? "dm" : "group";
|
|
581
583
|
results.push({
|
|
582
584
|
id: slug,
|
|
583
585
|
platformId: conv.id,
|
|
584
|
-
name: conv.title ?? "(untitled)",
|
|
585
|
-
type:
|
|
586
|
+
name: conv.type === "channel" ? `#${conv.name}` : conv.title ?? "(untitled)",
|
|
587
|
+
type: convType,
|
|
586
588
|
participantCount: participants.length
|
|
587
589
|
});
|
|
588
590
|
}
|
|
@@ -8,6 +8,9 @@ function slugify(text) {
|
|
|
8
8
|
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
9
9
|
}
|
|
10
10
|
function buildVoluteSlug(opts) {
|
|
11
|
+
if (opts.convType === "channel" && opts.convName) {
|
|
12
|
+
return `volute:#${opts.convName}`;
|
|
13
|
+
}
|
|
11
14
|
const isDM = opts.participants.length === 2;
|
|
12
15
|
if (isDM) {
|
|
13
16
|
const other = opts.participants.find((p) => p.username !== opts.mindUsername);
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ if (!process.env.VOLUTE_HOME) {
|
|
|
9
9
|
var command = process.argv[2];
|
|
10
10
|
var args = process.argv.slice(3);
|
|
11
11
|
if (command === "--version" || command === "-v") {
|
|
12
|
-
const { default: pkg } = await import("./package-
|
|
12
|
+
const { default: pkg } = await import("./package-OKLFO7UY.js");
|
|
13
13
|
console.log(pkg.version);
|
|
14
14
|
process.exit(0);
|
|
15
15
|
}
|
|
@@ -18,10 +18,10 @@ switch (command) {
|
|
|
18
18
|
await import("./mind-OJN6RBZW.js").then((m) => m.run(args));
|
|
19
19
|
break;
|
|
20
20
|
case "send":
|
|
21
|
-
await import("./send-
|
|
21
|
+
await import("./send-BNDTLUPM.js").then((m) => m.run(args));
|
|
22
22
|
break;
|
|
23
23
|
case "history":
|
|
24
|
-
await import("./history-
|
|
24
|
+
await import("./history-YUEKTJ2N.js").then((m) => m.run(args));
|
|
25
25
|
break;
|
|
26
26
|
case "variant":
|
|
27
27
|
await import("./variant-X5QFG6KK.js").then((m) => m.run(args));
|
|
@@ -35,17 +35,20 @@ switch (command) {
|
|
|
35
35
|
case "schedule":
|
|
36
36
|
await import("./schedule-AGYLDMNS.js").then((m) => m.run(args));
|
|
37
37
|
break;
|
|
38
|
+
case "skill":
|
|
39
|
+
await import("./skill-2Y42P4JY.js").then((m) => m.run(args));
|
|
40
|
+
break;
|
|
38
41
|
case "env":
|
|
39
42
|
await import("./env-6IDWGBUH.js").then((m) => m.run(args));
|
|
40
43
|
break;
|
|
41
44
|
case "up":
|
|
42
|
-
await import("./up-
|
|
45
|
+
await import("./up-7B3BWF2U.js").then((m) => m.run(args));
|
|
43
46
|
break;
|
|
44
47
|
case "down":
|
|
45
48
|
await import("./down-A56B5JLK.js").then((m) => m.run(args));
|
|
46
49
|
break;
|
|
47
50
|
case "restart":
|
|
48
|
-
await import("./daemon-restart-
|
|
51
|
+
await import("./daemon-restart-2HVTHZAT.js").then((m) => m.run(args));
|
|
49
52
|
break;
|
|
50
53
|
case "setup":
|
|
51
54
|
await import("./setup-DJKIZKGW.js").then((m) => m.run(args));
|
|
@@ -114,6 +117,15 @@ Commands:
|
|
|
114
117
|
volute schedule add ... Add a cron schedule
|
|
115
118
|
volute schedule remove ... Remove a schedule
|
|
116
119
|
|
|
120
|
+
volute skill list List shared skills
|
|
121
|
+
volute skill list --mind <name> List installed skills for a mind
|
|
122
|
+
volute skill info <name> Show details of a shared skill
|
|
123
|
+
volute skill install <name> --mind Install a shared skill into a mind
|
|
124
|
+
volute skill update <name> --mind Update an installed skill
|
|
125
|
+
volute skill publish <name> --mind Publish a mind's skill to shared repo
|
|
126
|
+
volute skill remove <name> Remove a shared skill
|
|
127
|
+
volute skill uninstall <name> --mind Uninstall a skill from a mind
|
|
128
|
+
|
|
117
129
|
volute env <set|get|list|remove> Manage environment variables
|
|
118
130
|
|
|
119
131
|
volute up [--port N] Start the daemon (default: 4200)
|
|
@@ -143,7 +155,7 @@ Options:
|
|
|
143
155
|
--version, -v Show version number
|
|
144
156
|
--help, -h Show this help message
|
|
145
157
|
|
|
146
|
-
Mind-scoped commands (send, history, variant, connector, schedule, channel, pages)
|
|
158
|
+
Mind-scoped commands (send, history, variant, connector, schedule, channel, skill, pages)
|
|
147
159
|
use --mind <name> or VOLUTE_MIND env var to identify the mind.`);
|
|
148
160
|
break;
|
|
149
161
|
default:
|
package/dist/connectors/slack.js
CHANGED