volute 0.11.4 → 0.13.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.
Files changed (61) hide show
  1. package/dist/{agent-2AQPI3QV.js → agent-HYX2ZTFM.js} +10 -10
  2. package/dist/{agent-manager-AZUDAKCP.js → agent-manager-HHBAAL2D.js} +4 -4
  3. package/dist/{channel-2WHBRDTD.js → channel-72YET5JC.js} +2 -2
  4. package/dist/{chunk-QF22MYDJ.js → chunk-BJDLYTPS.js} +1 -1
  5. package/dist/{chunk-46S7YHUB.js → chunk-EN3NHRQC.js} +12 -19
  6. package/dist/{chunk-DP2DX4WV.js → chunk-ESTOWEG2.js} +12 -3
  7. package/dist/{chunk-N6MLQ26B.js → chunk-GPZCPGV3.js} +11 -1
  8. package/dist/{chunk-ZKNBD5P3.js → chunk-IELXXS7E.js} +2 -2
  9. package/dist/{chunk-STOEJOJO.js → chunk-J3IHIXDB.js} +1 -1
  10. package/dist/{chunk-R3VB7NF5.js → chunk-KYHC7LHS.js} +2 -2
  11. package/dist/{chunk-YY2QX2J6.js → chunk-LG4ROCHN.js} +1 -1
  12. package/dist/{chunk-YTOPX4PB.js → chunk-NQKKTRET.js} +28 -37
  13. package/dist/{chunk-D5EVQTGJ.js → chunk-PEQQ7MRI.js} +2 -2
  14. package/dist/{chunk-LIPPXNIE.js → chunk-PTK3GBCG.js} +17 -18
  15. package/dist/{chunk-RGWADNLT.js → chunk-RH3XLDY2.js} +2 -2
  16. package/dist/{chunk-WTJI3JVR.js → chunk-VRAOTXDF.js} +9 -6
  17. package/dist/chunk-XUA3JUFK.js +121 -0
  18. package/dist/cli.js +26 -17
  19. package/dist/{connector-L2HBLZBW.js → connector-Z5KYVTZ5.js} +2 -2
  20. package/dist/connectors/discord.js +2 -2
  21. package/dist/connectors/slack.js +2 -2
  22. package/dist/connectors/telegram.js +2 -2
  23. package/dist/{create-VBZZNJOG.js → create-AAI52BC2.js} +1 -1
  24. package/dist/{daemon-client-P44NU3KU.js → daemon-client-WXN43USO.js} +2 -2
  25. package/dist/{daemon-restart-5W5AGBZ2.js → daemon-restart-4OXIGWV6.js} +6 -5
  26. package/dist/daemon.js +533 -557
  27. package/dist/{delete-BOTVU4YO.js → delete-BJ3LNU2I.js} +1 -1
  28. package/dist/down-IMZE7V42.js +14 -0
  29. package/dist/{env-CGORIKVF.js → env-EOO2C7L7.js} +2 -2
  30. package/dist/{history-NI5QP27M.js → history-J7TURCZS.js} +2 -2
  31. package/dist/{import-2BZUWT23.js → import-TKF67X4R.js} +3 -3
  32. package/dist/{logs-APWVWGNX.js → logs-KHBOS6IZ.js} +2 -2
  33. package/dist/{package-KVUXPTEW.js → package-TPXKJXLG.js} +1 -1
  34. package/dist/{restart-CCYM3MEC.js → restart-CMP63H6A.js} +2 -2
  35. package/dist/{schedule-E4MFGYSA.js → schedule-YMAJZ52M.js} +2 -2
  36. package/dist/seed-M6QPHFTV.js +68 -0
  37. package/dist/{send-X6OQGSD6.js → send-EZYEIVMA.js} +18 -6
  38. package/dist/{service-UL3OCODG.js → service-DDFZA7Q3.js} +4 -3
  39. package/dist/{setup-7N4KYOYN.js → setup-DCZWBBD7.js} +7 -7
  40. package/dist/sprout-EAWETTWZ.js +89 -0
  41. package/dist/{start-6YRS6FF6.js → start-54JXJ7VV.js} +2 -2
  42. package/dist/{status-DFWM342I.js → status-PMMS4XUH.js} +7 -5
  43. package/dist/{stop-UQSNF4CG.js → stop-WXU42NWP.js} +2 -2
  44. package/dist/{up-365HL7UT.js → up-N2OWDCT6.js} +5 -4
  45. package/dist/{update-PV3XM6DX.js → update-FXLGIIWH.js} +5 -4
  46. package/dist/{update-check-YPGH5X4E.js → update-check-NBHTSTHK.js} +2 -2
  47. package/dist/{upgrade-RSE4CZNE.js → upgrade-TEI7N6CQ.js} +1 -1
  48. package/dist/{variant-7IZF6OWO.js → variant-ZCZS3JAP.js} +4 -4
  49. package/dist/web-assets/assets/index-TqXd1QOX.js +307 -0
  50. package/dist/web-assets/index.html +1 -1
  51. package/package.json +1 -1
  52. package/templates/_base/_skills/orientation/SKILL.md +58 -0
  53. package/templates/_base/home/.config/config.json.tmpl +3 -0
  54. package/templates/_base/src/lib/startup.ts +8 -4
  55. package/templates/agent-sdk/volute-template.json +1 -1
  56. package/templates/pi/home/.config/config.json.tmpl +3 -0
  57. package/templates/pi/volute-template.json +1 -1
  58. package/dist/down-O2EQJ5DO.js +0 -13
  59. package/dist/web-assets/assets/index-D-3zx6vs.js +0 -307
  60. package/templates/_base/home/.config/volute.json.tmpl +0 -3
  61. package/templates/pi/home/.config/volute.json.tmpl +0 -3
package/dist/daemon.js CHANGED
@@ -1,4 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ applyInitFiles,
4
+ composeTemplate,
5
+ copyTemplateToDir,
6
+ findTemplatesRoot,
7
+ listFiles
8
+ } from "./chunk-XUA3JUFK.js";
2
9
  import {
3
10
  RotatingLog,
4
11
  clearJsonMap,
@@ -6,16 +13,7 @@ import {
6
13
  initAgentManager,
7
14
  loadJsonMap,
8
15
  saveJsonMap
9
- } from "./chunk-YTOPX4PB.js";
10
- import {
11
- applyIsolation,
12
- chownAgentDir,
13
- createAgentUser,
14
- deleteAgentUser,
15
- ensureVoluteGroup,
16
- getAgentUserIds,
17
- isIsolationEnabled
18
- } from "./chunk-46S7YHUB.js";
16
+ } from "./chunk-NQKKTRET.js";
19
17
  import {
20
18
  findOpenClawSession,
21
19
  importOpenClawConnectors,
@@ -23,33 +21,41 @@ import {
23
21
  parseNameFromIdentity,
24
22
  readVoluteConfig,
25
23
  writeVoluteConfig
26
- } from "./chunk-R3VB7NF5.js";
24
+ } from "./chunk-KYHC7LHS.js";
27
25
  import {
28
26
  agentEnvPath,
29
27
  loadMergedEnv,
30
28
  readEnv,
31
29
  sharedEnvPath,
32
30
  writeEnv
33
- } from "./chunk-QF22MYDJ.js";
31
+ } from "./chunk-BJDLYTPS.js";
34
32
  import {
35
33
  CHANNELS,
36
34
  getChannelDriver
37
- } from "./chunk-LIPPXNIE.js";
35
+ } from "./chunk-PTK3GBCG.js";
38
36
  import {
39
37
  exec,
40
38
  gitExec,
41
39
  resolveVoluteBin
42
- } from "./chunk-WTJI3JVR.js";
40
+ } from "./chunk-VRAOTXDF.js";
41
+ import {
42
+ chownAgentDir,
43
+ createAgentUser,
44
+ deleteAgentUser,
45
+ ensureVoluteGroup,
46
+ isIsolationEnabled,
47
+ wrapForIsolation
48
+ } from "./chunk-EN3NHRQC.js";
43
49
  import {
44
50
  checkForUpdate,
45
51
  checkForUpdateCached,
46
52
  getCurrentVersion
47
- } from "./chunk-YY2QX2J6.js";
53
+ } from "./chunk-LG4ROCHN.js";
48
54
  import "./chunk-D424ZQGI.js";
49
55
  import {
50
- slugify,
56
+ buildVoluteSlug,
51
57
  writeChannelEntry
52
- } from "./chunk-N6MLQ26B.js";
58
+ } from "./chunk-GPZCPGV3.js";
53
59
  import {
54
60
  addAgent,
55
61
  addVariant,
@@ -72,16 +78,16 @@ import {
72
78
  validateAgentName,
73
79
  validateBranchName,
74
80
  voluteHome
75
- } from "./chunk-DP2DX4WV.js";
81
+ } from "./chunk-ESTOWEG2.js";
76
82
  import {
77
83
  __export
78
84
  } from "./chunk-K3NQKI34.js";
79
85
 
80
86
  // src/daemon.ts
81
87
  import { randomBytes } from "crypto";
82
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync9, unlinkSync as unlinkSync2, writeFileSync as writeFileSync7 } from "fs";
88
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync8, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "fs";
83
89
  import { homedir as homedir2 } from "os";
84
- import { resolve as resolve16 } from "path";
90
+ import { resolve as resolve15 } from "path";
85
91
  import { format } from "util";
86
92
 
87
93
  // src/lib/connector-manager.ts
@@ -248,8 +254,14 @@ var ConnectorManager = class {
248
254
  const logsDir = resolve2(agentStateDir, "logs");
249
255
  mkdirSync(logsDir, { recursive: true });
250
256
  if (isIsolationEnabled()) {
251
- const [base] = agentName.split("@", 2);
252
- chownAgentDir(agentStateDir, base);
257
+ try {
258
+ const [base] = agentName.split("@", 2);
259
+ chownAgentDir(agentStateDir, base);
260
+ } catch (err) {
261
+ throw new Error(
262
+ `Cannot start connector ${type} for ${agentName}: failed to set ownership on state directory ${agentStateDir}: ${err instanceof Error ? err.message : err}`
263
+ );
264
+ }
253
265
  }
254
266
  const logStream = new RotatingLog(resolve2(logsDir, `${type}.log`));
255
267
  const agentEnv = loadMergedEnv(agentName);
@@ -271,8 +283,8 @@ var ConnectorManager = class {
271
283
  ...connectorEnv
272
284
  }
273
285
  };
274
- await applyIsolation(spawnOpts, agentName);
275
- const child = spawn(runtime, [connectorScript], spawnOpts);
286
+ const [spawnCmd, spawnArgs] = wrapForIsolation(runtime, [connectorScript], agentName);
287
+ const child = spawn(spawnCmd, spawnArgs, spawnOpts);
276
288
  let lastStderr = "";
277
289
  child.stdout?.pipe(logStream);
278
290
  child.stderr?.on("data", (chunk) => {
@@ -326,19 +338,19 @@ var ConnectorManager = class {
326
338
  const stopKey = `${agentName}:${type}`;
327
339
  this.stopping.add(stopKey);
328
340
  agentMap.delete(type);
329
- await new Promise((resolve17) => {
330
- tracked.child.on("exit", () => resolve17());
341
+ await new Promise((resolve16) => {
342
+ tracked.child.on("exit", () => resolve16());
331
343
  try {
332
344
  tracked.child.kill("SIGTERM");
333
345
  } catch {
334
- resolve17();
346
+ resolve16();
335
347
  }
336
348
  setTimeout(() => {
337
349
  try {
338
350
  tracked.child.kill("SIGKILL");
339
351
  } catch {
340
352
  }
341
- resolve17();
353
+ resolve16();
342
354
  }, 5e3);
343
355
  });
344
356
  this.stopping.delete(stopKey);
@@ -1048,9 +1060,9 @@ var authMiddleware = createMiddleware(async (c, next) => {
1048
1060
  });
1049
1061
 
1050
1062
  // src/web/server.ts
1051
- import { existsSync as existsSync10 } from "fs";
1063
+ import { existsSync as existsSync9 } from "fs";
1052
1064
  import { readFile as readFile2, stat } from "fs/promises";
1053
- import { dirname as dirname4, extname, resolve as resolve15 } from "path";
1065
+ import { dirname as dirname3, extname, resolve as resolve14 } from "path";
1054
1066
  import { serve } from "@hono/node-server";
1055
1067
 
1056
1068
  // src/lib/log-buffer.ts
@@ -1105,17 +1117,19 @@ import { HTTPException } from "hono/http-exception";
1105
1117
 
1106
1118
  // src/web/routes/agents.ts
1107
1119
  import {
1108
- cpSync as cpSync2,
1109
- existsSync as existsSync6,
1110
- mkdirSync as mkdirSync5,
1111
- readdirSync as readdirSync4,
1112
- readFileSync as readFileSync6,
1113
- rmSync as rmSync2,
1114
- writeFileSync as writeFileSync5
1120
+ cpSync,
1121
+ existsSync as existsSync5,
1122
+ mkdirSync as mkdirSync4,
1123
+ readdirSync as readdirSync3,
1124
+ readFileSync as readFileSync5,
1125
+ rmSync,
1126
+ writeFileSync as writeFileSync4
1115
1127
  } from "fs";
1116
- import { resolve as resolve9 } from "path";
1117
- import { and as and2, desc, eq as eq3 } from "drizzle-orm";
1128
+ import { resolve as resolve8 } from "path";
1129
+ import { zValidator } from "@hono/zod-validator";
1130
+ import { and as and3, desc as desc2, eq as eq4, sql as sql3 } from "drizzle-orm";
1118
1131
  import { Hono } from "hono";
1132
+ import { z } from "zod";
1119
1133
 
1120
1134
  // src/lib/consolidate.ts
1121
1135
  import { readdirSync as readdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
@@ -1188,14 +1202,209 @@ ${content2}`);
1188
1202
  }
1189
1203
  }
1190
1204
 
1191
- // src/lib/convert-session.ts
1205
+ // src/lib/conversations.ts
1192
1206
  import { randomUUID } from "crypto";
1207
+ import { and as and2, desc, eq as eq3, inArray, isNull, sql as sql2 } from "drizzle-orm";
1208
+
1209
+ // src/lib/conversation-events.ts
1210
+ var subscribers = /* @__PURE__ */ new Map();
1211
+ function subscribe(conversationId, callback) {
1212
+ let set = subscribers.get(conversationId);
1213
+ if (!set) {
1214
+ set = /* @__PURE__ */ new Set();
1215
+ subscribers.set(conversationId, set);
1216
+ }
1217
+ set.add(callback);
1218
+ return () => {
1219
+ set.delete(callback);
1220
+ if (set.size === 0) subscribers.delete(conversationId);
1221
+ };
1222
+ }
1223
+ function publish(conversationId, event) {
1224
+ const set = subscribers.get(conversationId);
1225
+ if (!set) return;
1226
+ for (const cb of set) {
1227
+ try {
1228
+ cb(event);
1229
+ } catch (err) {
1230
+ console.error("[conversation-events] subscriber threw:", err);
1231
+ set.delete(cb);
1232
+ if (set.size === 0) subscribers.delete(conversationId);
1233
+ }
1234
+ }
1235
+ }
1236
+
1237
+ // src/lib/conversations.ts
1238
+ async function createConversation(agentName, channel, opts) {
1239
+ const db2 = await getDb();
1240
+ const id = randomUUID();
1241
+ await db2.insert(conversations).values({
1242
+ id,
1243
+ agent_name: agentName,
1244
+ channel,
1245
+ user_id: opts?.userId ?? null,
1246
+ title: opts?.title ?? null
1247
+ });
1248
+ if (opts?.participantIds && opts.participantIds.length > 0) {
1249
+ await db2.insert(conversationParticipants).values(
1250
+ opts.participantIds.map((uid, i) => ({
1251
+ conversation_id: id,
1252
+ user_id: uid,
1253
+ role: i === 0 ? "owner" : "member"
1254
+ }))
1255
+ );
1256
+ }
1257
+ return {
1258
+ id,
1259
+ agent_name: agentName,
1260
+ channel,
1261
+ user_id: opts?.userId ?? null,
1262
+ title: opts?.title ?? null,
1263
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
1264
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
1265
+ };
1266
+ }
1267
+ async function getConversation(id) {
1268
+ const db2 = await getDb();
1269
+ const row = await db2.select().from(conversations).where(eq3(conversations.id, id)).get();
1270
+ return row ?? null;
1271
+ }
1272
+ async function getParticipants(conversationId) {
1273
+ const db2 = await getDb();
1274
+ const rows = await db2.select({
1275
+ userId: conversationParticipants.user_id,
1276
+ username: users.username,
1277
+ userType: users.user_type,
1278
+ role: conversationParticipants.role
1279
+ }).from(conversationParticipants).innerJoin(users, eq3(conversationParticipants.user_id, users.id)).where(eq3(conversationParticipants.conversation_id, conversationId)).all();
1280
+ return rows;
1281
+ }
1282
+ async function isParticipant(conversationId, userId) {
1283
+ const db2 = await getDb();
1284
+ const row = await db2.select({ user_id: conversationParticipants.user_id }).from(conversationParticipants).where(
1285
+ and2(
1286
+ eq3(conversationParticipants.conversation_id, conversationId),
1287
+ eq3(conversationParticipants.user_id, userId)
1288
+ )
1289
+ ).get();
1290
+ return row != null;
1291
+ }
1292
+ async function listConversationsForUser(userId) {
1293
+ const db2 = await getDb();
1294
+ const participantRows = await db2.select({ conversation_id: conversationParticipants.conversation_id }).from(conversationParticipants).where(eq3(conversationParticipants.user_id, userId)).all();
1295
+ if (participantRows.length === 0) return [];
1296
+ const convIds = participantRows.map((r) => r.conversation_id);
1297
+ return db2.select().from(conversations).where(inArray(conversations.id, convIds)).orderBy(desc(conversations.updated_at)).all();
1298
+ }
1299
+ async function isParticipantOrOwner(conversationId, userId) {
1300
+ if (await isParticipant(conversationId, userId)) return true;
1301
+ const db2 = await getDb();
1302
+ const row = await db2.select().from(conversations).where(and2(eq3(conversations.id, conversationId), eq3(conversations.user_id, userId))).get();
1303
+ return row != null;
1304
+ }
1305
+ async function deleteConversationForUser(id, userId) {
1306
+ if (!await isParticipantOrOwner(id, userId)) return false;
1307
+ await deleteConversation(id);
1308
+ return true;
1309
+ }
1310
+ async function addMessage(conversationId, role, senderName, content) {
1311
+ const db2 = await getDb();
1312
+ const serialized = JSON.stringify(content);
1313
+ const [result] = await db2.insert(messages).values({ conversation_id: conversationId, role, sender_name: senderName, content: serialized }).returning({ id: messages.id, created_at: messages.created_at });
1314
+ await db2.update(conversations).set({ updated_at: sql2`datetime('now')` }).where(eq3(conversations.id, conversationId));
1315
+ if (role === "user") {
1316
+ const firstText = content.find((b) => b.type === "text");
1317
+ const title = firstText ? firstText.text.slice(0, 80) : "";
1318
+ if (title) {
1319
+ await db2.update(conversations).set({ title }).where(and2(eq3(conversations.id, conversationId), isNull(conversations.title)));
1320
+ }
1321
+ }
1322
+ const msg = {
1323
+ id: result.id,
1324
+ conversation_id: conversationId,
1325
+ role,
1326
+ sender_name: senderName,
1327
+ content,
1328
+ created_at: result.created_at
1329
+ };
1330
+ publish(conversationId, {
1331
+ type: "message",
1332
+ id: msg.id,
1333
+ role: msg.role,
1334
+ senderName: msg.sender_name,
1335
+ content: msg.content,
1336
+ createdAt: msg.created_at
1337
+ });
1338
+ return msg;
1339
+ }
1340
+ async function getMessages(conversationId) {
1341
+ const db2 = await getDb();
1342
+ const rows = await db2.select().from(messages).where(eq3(messages.conversation_id, conversationId)).orderBy(messages.created_at).all();
1343
+ return rows.map((row) => {
1344
+ let content;
1345
+ try {
1346
+ const parsed = JSON.parse(row.content);
1347
+ content = Array.isArray(parsed) ? parsed : [{ type: "text", text: row.content }];
1348
+ } catch {
1349
+ content = [{ type: "text", text: row.content }];
1350
+ }
1351
+ return { ...row, content };
1352
+ });
1353
+ }
1354
+ async function listConversationsWithParticipants(userId) {
1355
+ const convs = await listConversationsForUser(userId);
1356
+ if (convs.length === 0) return [];
1357
+ const db2 = await getDb();
1358
+ const convIds = convs.map((c) => c.id);
1359
+ const rows = await db2.select({
1360
+ conversationId: conversationParticipants.conversation_id,
1361
+ userId: users.id,
1362
+ username: users.username,
1363
+ userType: users.user_type,
1364
+ role: conversationParticipants.role
1365
+ }).from(conversationParticipants).innerJoin(users, eq3(conversationParticipants.user_id, users.id)).where(inArray(conversationParticipants.conversation_id, convIds));
1366
+ const byConv = /* @__PURE__ */ new Map();
1367
+ for (const r of rows) {
1368
+ let arr = byConv.get(r.conversationId);
1369
+ if (!arr) {
1370
+ arr = [];
1371
+ byConv.set(r.conversationId, arr);
1372
+ }
1373
+ arr.push({
1374
+ userId: r.userId,
1375
+ username: r.username,
1376
+ userType: r.userType,
1377
+ role: r.role
1378
+ });
1379
+ }
1380
+ return convs.map((c) => ({ ...c, participants: byConv.get(c.id) ?? [] }));
1381
+ }
1382
+ async function findDMConversation(agentName, participantIds) {
1383
+ const db2 = await getDb();
1384
+ const agentConvs = await db2.select({ id: conversations.id }).from(conversations).where(eq3(conversations.agent_name, agentName)).all();
1385
+ for (const conv of agentConvs) {
1386
+ const rows = await db2.select({ user_id: conversationParticipants.user_id }).from(conversationParticipants).where(eq3(conversationParticipants.conversation_id, conv.id)).all();
1387
+ if (rows.length !== 2) continue;
1388
+ const ids = new Set(rows.map((r) => r.user_id));
1389
+ if (ids.has(participantIds[0]) && ids.has(participantIds[1])) {
1390
+ return conv.id;
1391
+ }
1392
+ }
1393
+ return null;
1394
+ }
1395
+ async function deleteConversation(id) {
1396
+ const db2 = await getDb();
1397
+ await db2.delete(conversations).where(eq3(conversations.id, id));
1398
+ }
1399
+
1400
+ // src/lib/convert-session.ts
1401
+ import { randomUUID as randomUUID2 } from "crypto";
1193
1402
  import { mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
1194
1403
  import { homedir } from "os";
1195
1404
  import { resolve as resolve7 } from "path";
1196
1405
  function convertSession(opts) {
1197
1406
  const lines = readFileSync4(opts.sessionPath, "utf-8").trim().split("\n");
1198
- const sessionId = randomUUID();
1407
+ const sessionId = randomUUID2();
1199
1408
  const idMap = /* @__PURE__ */ new Map();
1200
1409
  const messages2 = [];
1201
1410
  for (const line of lines) {
@@ -1210,7 +1419,7 @@ function convertSession(opts) {
1210
1419
  const event = messages2[i];
1211
1420
  const msg = event.message;
1212
1421
  if (msg.role === "user") {
1213
- const uuid = randomUUID();
1422
+ const uuid = randomUUID2();
1214
1423
  idMap.set(event.id, uuid);
1215
1424
  const parentUuid = event.parentId ? idMap.get(event.parentId) ?? null : null;
1216
1425
  const sdkEvent = {
@@ -1234,7 +1443,7 @@ function convertSession(opts) {
1234
1443
  } else if (msg.role === "assistant") {
1235
1444
  const content = convertAssistantContent(msg.content);
1236
1445
  if (content.length === 0) continue;
1237
- const uuid = randomUUID();
1446
+ const uuid = randomUUID2();
1238
1447
  idMap.set(event.id, uuid);
1239
1448
  const parentUuid = event.parentId ? idMap.get(event.parentId) ?? null : null;
1240
1449
  const stopReason = mapStopReason(msg.stopReason);
@@ -1249,12 +1458,12 @@ function convertSession(opts) {
1249
1458
  isSidechain: false,
1250
1459
  userType: "external",
1251
1460
  type: "assistant",
1252
- requestId: `req_imported_${randomUUID()}`,
1461
+ requestId: `req_imported_${randomUUID2()}`,
1253
1462
  message: {
1254
1463
  role: "assistant",
1255
1464
  content,
1256
1465
  type: "message",
1257
- id: `msg_imported_${randomUUID()}`,
1466
+ id: `msg_imported_${randomUUID2()}`,
1258
1467
  model: mapModel(msg.model),
1259
1468
  stop_reason: stopReason,
1260
1469
  stop_sequence: null,
@@ -1282,7 +1491,7 @@ function convertSession(opts) {
1282
1491
  j++;
1283
1492
  }
1284
1493
  i = j - 1;
1285
- const uuid = randomUUID();
1494
+ const uuid = randomUUID2();
1286
1495
  idMap.set(lastToolResultId, uuid);
1287
1496
  const parentUuid = event.parentId ? idMap.get(event.parentId) ?? null : lastSdkUuid;
1288
1497
  const sdkEvent = {
@@ -1362,118 +1571,6 @@ function convertAssistantContent(content) {
1362
1571
  return result;
1363
1572
  }
1364
1573
 
1365
- // src/lib/template.ts
1366
- import {
1367
- cpSync,
1368
- existsSync as existsSync5,
1369
- mkdirSync as mkdirSync4,
1370
- readdirSync as readdirSync3,
1371
- readFileSync as readFileSync5,
1372
- renameSync,
1373
- rmSync,
1374
- statSync,
1375
- writeFileSync as writeFileSync4
1376
- } from "fs";
1377
- import { tmpdir } from "os";
1378
- import { dirname as dirname3, join, relative, resolve as resolve8 } from "path";
1379
- function findTemplatesRoot() {
1380
- let dir = dirname3(new URL(import.meta.url).pathname);
1381
- for (let i = 0; i < 5; i++) {
1382
- const candidate = resolve8(dir, "templates");
1383
- if (existsSync5(resolve8(candidate, "_base"))) return candidate;
1384
- dir = dirname3(dir);
1385
- }
1386
- console.error(
1387
- "Templates directory not found. Searched up from:",
1388
- dirname3(new URL(import.meta.url).pathname)
1389
- );
1390
- process.exit(1);
1391
- }
1392
- function composeTemplate(templatesRoot, templateName) {
1393
- const baseDir = resolve8(templatesRoot, "_base");
1394
- const templateDir = resolve8(templatesRoot, templateName);
1395
- if (!existsSync5(baseDir)) {
1396
- console.error("Base template not found:", baseDir);
1397
- process.exit(1);
1398
- }
1399
- if (!existsSync5(templateDir)) {
1400
- console.error(`Template not found: ${templateName}`);
1401
- process.exit(1);
1402
- }
1403
- const composedDir = resolve8(tmpdir(), `volute-template-${Date.now()}`);
1404
- mkdirSync4(composedDir, { recursive: true });
1405
- cpSync(baseDir, composedDir, { recursive: true });
1406
- for (const file of listFiles(templateDir)) {
1407
- const src = resolve8(templateDir, file);
1408
- const dest = resolve8(composedDir, file);
1409
- mkdirSync4(dirname3(dest), { recursive: true });
1410
- cpSync(src, dest);
1411
- }
1412
- const manifestPath = resolve8(composedDir, "volute-template.json");
1413
- if (!existsSync5(manifestPath)) {
1414
- rmSync(composedDir, { recursive: true, force: true });
1415
- console.error(`Template manifest not found: ${templateName}/volute-template.json`);
1416
- process.exit(1);
1417
- }
1418
- const manifest = JSON.parse(readFileSync5(manifestPath, "utf-8"));
1419
- const skillsSrc = resolve8(composedDir, "_skills");
1420
- if (existsSync5(skillsSrc)) {
1421
- const skillsDest = resolve8(composedDir, manifest.skillsDir);
1422
- mkdirSync4(skillsDest, { recursive: true });
1423
- cpSync(skillsSrc, skillsDest, { recursive: true });
1424
- rmSync(skillsSrc, { recursive: true, force: true });
1425
- }
1426
- rmSync(manifestPath);
1427
- return { composedDir, manifest };
1428
- }
1429
- function copyTemplateToDir(composedDir, destDir, agentName, manifest) {
1430
- cpSync(composedDir, destDir, { recursive: true });
1431
- for (const [from, to] of Object.entries(manifest.rename)) {
1432
- const fromPath = resolve8(destDir, from);
1433
- if (existsSync5(fromPath)) {
1434
- renameSync(fromPath, resolve8(destDir, to));
1435
- }
1436
- }
1437
- for (const file of manifest.substitute) {
1438
- const path = resolve8(destDir, file);
1439
- if (existsSync5(path)) {
1440
- const content = readFileSync5(path, "utf-8");
1441
- writeFileSync4(path, content.replaceAll("{{name}}", agentName));
1442
- }
1443
- }
1444
- }
1445
- function applyInitFiles(destDir) {
1446
- const initDir = resolve8(destDir, ".init");
1447
- if (!existsSync5(initDir)) return;
1448
- const homeDir = resolve8(destDir, "home");
1449
- for (const file of listFiles(initDir)) {
1450
- const src = resolve8(initDir, file);
1451
- const dest = resolve8(homeDir, file);
1452
- const parent = dirname3(dest);
1453
- if (!existsSync5(parent)) {
1454
- mkdirSync4(parent, { recursive: true });
1455
- }
1456
- cpSync(src, dest);
1457
- }
1458
- rmSync(initDir, { recursive: true, force: true });
1459
- }
1460
- function listFiles(dir) {
1461
- const results = [];
1462
- function walk(current) {
1463
- for (const entry of readdirSync3(current)) {
1464
- const full = join(current, entry);
1465
- if (statSync(full).isDirectory()) {
1466
- if (entry === ".git") continue;
1467
- walk(full);
1468
- } else {
1469
- results.push(relative(dir, full));
1470
- }
1471
- }
1472
- }
1473
- walk(dir);
1474
- return results;
1475
- }
1476
-
1477
1574
  // src/lib/typing.ts
1478
1575
  var DEFAULT_TTL_MS = 1e4;
1479
1576
  var SWEEP_INTERVAL_MS = 5e3;
@@ -1545,6 +1642,7 @@ function getTypingMap() {
1545
1642
  async function startAgentFull(name, baseName, variantName) {
1546
1643
  await getAgentManager().startAgent(name);
1547
1644
  if (variantName) return;
1645
+ if (findAgent(baseName)?.stage === "seed") return;
1548
1646
  const dir = agentDir(baseName);
1549
1647
  const entry = findAgent(baseName);
1550
1648
  await getConnectorManager().startConnectors(baseName, dir, entry.port, getDaemonPort());
@@ -1567,7 +1665,7 @@ function extractTextContent(content) {
1567
1665
  }
1568
1666
  function getDaemonPort() {
1569
1667
  try {
1570
- const data = JSON.parse(readFileSync6(resolve9(voluteHome(), "daemon.json"), "utf-8"));
1668
+ const data = JSON.parse(readFileSync5(resolve8(voluteHome(), "daemon.json"), "utf-8"));
1571
1669
  return data.port;
1572
1670
  } catch (err) {
1573
1671
  if (err?.code !== "ENOENT") {
@@ -1607,9 +1705,9 @@ async function getAgentStatus(name, port) {
1607
1705
  return { status, channels };
1608
1706
  }
1609
1707
  var TEMPLATE_BRANCH = "volute/template";
1610
- async function initTemplateBranch(projectRoot, composedDir, manifest, ids, env) {
1708
+ async function initTemplateBranch(projectRoot, composedDir, manifest, agentName, env) {
1611
1709
  const templateFiles = listFiles(composedDir).filter((f) => !f.startsWith(".init/") && !f.startsWith(".init\\")).map((f) => manifest.rename[f] ?? f);
1612
- const opts = { cwd: projectRoot, uid: ids?.uid, gid: ids?.gid, env };
1710
+ const opts = { cwd: projectRoot, agentName, env };
1613
1711
  await gitExec(["checkout", "--orphan", TEMPLATE_BRANCH], opts);
1614
1712
  await gitExec(["add", "--", ...templateFiles], opts);
1615
1713
  await gitExec(["commit", "-m", "template update"], opts);
@@ -1618,7 +1716,7 @@ async function initTemplateBranch(projectRoot, composedDir, manifest, ids, env)
1618
1716
  await gitExec(["commit", "-m", "initial commit"], opts);
1619
1717
  }
1620
1718
  async function updateTemplateBranch(projectRoot, template, agentName) {
1621
- const tempWorktree = resolve9(projectRoot, ".variants", "_template_update");
1719
+ const tempWorktree = resolve8(projectRoot, ".variants", "_template_update");
1622
1720
  let branchExists = false;
1623
1721
  try {
1624
1722
  await gitExec(["rev-parse", "--verify", TEMPLATE_BRANCH], { cwd: projectRoot });
@@ -1629,8 +1727,8 @@ async function updateTemplateBranch(projectRoot, template, agentName) {
1629
1727
  await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
1630
1728
  } catch {
1631
1729
  }
1632
- if (existsSync6(tempWorktree)) {
1633
- rmSync2(tempWorktree, { recursive: true, force: true });
1730
+ if (existsSync5(tempWorktree)) {
1731
+ rmSync(tempWorktree, { recursive: true, force: true });
1634
1732
  }
1635
1733
  const templatesRoot = findTemplatesRoot();
1636
1734
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
@@ -1650,9 +1748,9 @@ async function updateTemplateBranch(projectRoot, template, agentName) {
1650
1748
  });
1651
1749
  }
1652
1750
  copyTemplateToDir(composedDir, tempWorktree, agentName, manifest);
1653
- const initDir = resolve9(tempWorktree, ".init");
1654
- if (existsSync6(initDir)) {
1655
- rmSync2(initDir, { recursive: true, force: true });
1751
+ const initDir = resolve8(tempWorktree, ".init");
1752
+ if (existsSync5(initDir)) {
1753
+ rmSync(initDir, { recursive: true, force: true });
1656
1754
  }
1657
1755
  await gitExec(["add", "-A"], { cwd: tempWorktree });
1658
1756
  try {
@@ -1665,10 +1763,10 @@ async function updateTemplateBranch(projectRoot, template, agentName) {
1665
1763
  await gitExec(["worktree", "remove", "--force", tempWorktree], { cwd: projectRoot });
1666
1764
  } catch {
1667
1765
  }
1668
- if (existsSync6(tempWorktree)) {
1669
- rmSync2(tempWorktree, { recursive: true, force: true });
1766
+ if (existsSync5(tempWorktree)) {
1767
+ rmSync(tempWorktree, { recursive: true, force: true });
1670
1768
  }
1671
- rmSync2(composedDir, { recursive: true, force: true });
1769
+ rmSync(composedDir, { recursive: true, force: true });
1672
1770
  }
1673
1771
  }
1674
1772
  async function mergeTemplateBranch(worktreeDir) {
@@ -1688,57 +1786,87 @@ async function mergeTemplateBranch(worktreeDir) {
1688
1786
  throw e;
1689
1787
  }
1690
1788
  }
1691
- var app = new Hono().post("/", requireAdmin, async (c) => {
1692
- let body;
1693
- try {
1694
- body = await c.req.json();
1695
- } catch {
1696
- return c.json({ error: "Invalid JSON" }, 400);
1697
- }
1789
+ var createAgentSchema = z.object({
1790
+ name: z.string(),
1791
+ template: z.string().optional(),
1792
+ stage: z.enum(["seed", "mind"]).optional(),
1793
+ description: z.string().optional(),
1794
+ model: z.string().optional()
1795
+ });
1796
+ var app = new Hono().post("/", requireAdmin, zValidator("json", createAgentSchema), async (c) => {
1797
+ const body = c.req.valid("json");
1698
1798
  const { name, template = "agent-sdk" } = body;
1699
1799
  const nameErr = validateAgentName(name);
1700
1800
  if (nameErr) return c.json({ error: nameErr }, 400);
1701
1801
  if (findAgent(name)) return c.json({ error: `Agent already exists: ${name}` }, 409);
1702
1802
  ensureVoluteHome();
1703
1803
  const dest = agentDir(name);
1704
- if (existsSync6(dest)) return c.json({ error: "Agent directory already exists" }, 409);
1804
+ if (existsSync5(dest)) return c.json({ error: "Agent directory already exists" }, 409);
1705
1805
  const templatesRoot = findTemplatesRoot();
1706
1806
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
1707
1807
  try {
1708
1808
  copyTemplateToDir(composedDir, dest, name, manifest);
1709
1809
  applyInitFiles(dest);
1810
+ if (body.model) {
1811
+ const configPath = resolve8(dest, "home/.config/config.json");
1812
+ const existing = existsSync5(configPath) ? JSON.parse(readFileSync5(configPath, "utf-8")) : {};
1813
+ existing.model = body.model;
1814
+ writeFileSync4(configPath, `${JSON.stringify(existing, null, 2)}
1815
+ `);
1816
+ }
1817
+ if (body.stage === "seed") {
1818
+ const descLine = body.description ? `
1819
+ The human who planted you described you as: "${body.description}"
1820
+ ` : "";
1821
+ writeFileSync4(
1822
+ resolve8(dest, "home/SOUL.md"),
1823
+ `You are ${name}.
1824
+
1825
+ You don't have a soul yet. You're a seed \u2014 a new agent in the process of discovering who you are.
1826
+ ${descLine}
1827
+ Have a conversation with the human. Explore what kind of agent you want to be. When you're ready, write your SOUL.md and MEMORY.md, then run \`volute sprout\` to complete the transformation.
1828
+ `
1829
+ );
1830
+ const skillsDir = resolve8(dest, manifest.skillsDir);
1831
+ for (const skill of ["volute-agent", "memory", "sessions"]) {
1832
+ const skillPath = resolve8(skillsDir, skill);
1833
+ if (existsSync5(skillPath)) rmSync(skillPath, { recursive: true, force: true });
1834
+ }
1835
+ }
1710
1836
  const port = nextPort();
1711
- addAgent(name, port);
1837
+ addAgent(name, port, body.stage);
1838
+ const homeDir = resolve8(dest, "home");
1712
1839
  ensureVoluteGroup();
1713
- createAgentUser(name);
1840
+ createAgentUser(name, homeDir);
1714
1841
  chownAgentDir(dest, name);
1715
- const ids = isIsolationEnabled() ? await getAgentUserIds(name) : void 0;
1716
- const env = ids ? { ...process.env, HOME: dest } : void 0;
1717
- await exec("npm", ["install"], { cwd: dest, uid: ids?.uid, gid: ids?.gid, env });
1842
+ const agentName = isIsolationEnabled() ? name : void 0;
1843
+ const env = agentName ? { ...process.env, HOME: homeDir } : void 0;
1844
+ await exec("npm", ["install"], { cwd: dest, agentName, env });
1718
1845
  let gitWarning;
1719
1846
  try {
1720
- await gitExec(["init"], { cwd: dest, uid: ids?.uid, gid: ids?.gid, env });
1721
- await initTemplateBranch(dest, composedDir, manifest, ids, env);
1847
+ await gitExec(["init"], { cwd: dest, agentName, env });
1848
+ await initTemplateBranch(dest, composedDir, manifest, agentName, env);
1722
1849
  } catch {
1723
- rmSync2(resolve9(dest, ".git"), { recursive: true, force: true });
1850
+ rmSync(resolve8(dest, ".git"), { recursive: true, force: true });
1724
1851
  gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
1725
1852
  }
1726
1853
  return c.json({
1727
1854
  ok: true,
1728
1855
  name,
1729
1856
  port,
1857
+ stage: body.stage ?? "mind",
1730
1858
  message: `Created agent: ${name} (port ${port})`,
1731
1859
  ...gitWarning && { warning: gitWarning }
1732
1860
  });
1733
1861
  } catch (err) {
1734
- if (existsSync6(dest)) rmSync2(dest, { recursive: true, force: true });
1862
+ if (existsSync5(dest)) rmSync(dest, { recursive: true, force: true });
1735
1863
  try {
1736
1864
  removeAgent(name);
1737
1865
  } catch {
1738
1866
  }
1739
1867
  return c.json({ error: err instanceof Error ? err.message : "Failed to create agent" }, 500);
1740
1868
  } finally {
1741
- rmSync2(composedDir, { recursive: true, force: true });
1869
+ rmSync(composedDir, { recursive: true, force: true });
1742
1870
  }
1743
1871
  }).post("/import", requireAdmin, async (c) => {
1744
1872
  let body;
@@ -1748,13 +1876,13 @@ var app = new Hono().post("/", requireAdmin, async (c) => {
1748
1876
  return c.json({ error: "Invalid JSON" }, 400);
1749
1877
  }
1750
1878
  const wsDir = body.workspacePath;
1751
- if (!wsDir || !existsSync6(resolve9(wsDir, "SOUL.md")) || !existsSync6(resolve9(wsDir, "IDENTITY.md"))) {
1879
+ if (!wsDir || !existsSync5(resolve8(wsDir, "SOUL.md")) || !existsSync5(resolve8(wsDir, "IDENTITY.md"))) {
1752
1880
  return c.json({ error: "Invalid workspace: missing SOUL.md or IDENTITY.md" }, 400);
1753
1881
  }
1754
- const soul = readFileSync6(resolve9(wsDir, "SOUL.md"), "utf-8");
1755
- const identity = readFileSync6(resolve9(wsDir, "IDENTITY.md"), "utf-8");
1756
- const userPath = resolve9(wsDir, "USER.md");
1757
- const user = existsSync6(userPath) ? readFileSync6(userPath, "utf-8") : "";
1882
+ const soul = readFileSync5(resolve8(wsDir, "SOUL.md"), "utf-8");
1883
+ const identity = readFileSync5(resolve8(wsDir, "IDENTITY.md"), "utf-8");
1884
+ const userPath = resolve8(wsDir, "USER.md");
1885
+ const user = existsSync5(userPath) ? readFileSync5(userPath, "utf-8") : "";
1758
1886
  const name = body.name ?? parseNameFromIdentity(identity) ?? "imported-agent";
1759
1887
  const template = body.template ?? "agent-sdk";
1760
1888
  const nameErr = validateAgentName(name);
@@ -1774,76 +1902,72 @@ ${user.trimEnd()}
1774
1902
  ` : "";
1775
1903
  ensureVoluteHome();
1776
1904
  const dest = agentDir(name);
1777
- if (existsSync6(dest)) return c.json({ error: "Agent directory already exists" }, 409);
1905
+ if (existsSync5(dest)) return c.json({ error: "Agent directory already exists" }, 409);
1778
1906
  const templatesRoot = findTemplatesRoot();
1779
1907
  const { composedDir, manifest } = composeTemplate(templatesRoot, template);
1780
1908
  try {
1781
1909
  copyTemplateToDir(composedDir, dest, name, manifest);
1782
1910
  applyInitFiles(dest);
1783
- writeFileSync5(resolve9(dest, "home/SOUL.md"), mergedSoul);
1784
- const wsMemoryPath = resolve9(wsDir, "MEMORY.md");
1785
- const hasMemory = existsSync6(wsMemoryPath);
1911
+ writeFileSync4(resolve8(dest, "home/SOUL.md"), mergedSoul);
1912
+ const wsMemoryPath = resolve8(wsDir, "MEMORY.md");
1913
+ const hasMemory = existsSync5(wsMemoryPath);
1786
1914
  if (hasMemory) {
1787
- const existingMemory = readFileSync6(wsMemoryPath, "utf-8");
1788
- writeFileSync5(
1789
- resolve9(dest, "home/MEMORY.md"),
1915
+ const existingMemory = readFileSync5(wsMemoryPath, "utf-8");
1916
+ writeFileSync4(
1917
+ resolve8(dest, "home/MEMORY.md"),
1790
1918
  `${existingMemory.trimEnd()}${mergedMemoryExtra}`
1791
1919
  );
1792
1920
  } else if (user) {
1793
- writeFileSync5(resolve9(dest, "home/MEMORY.md"), `${user.trimEnd()}
1921
+ writeFileSync4(resolve8(dest, "home/MEMORY.md"), `${user.trimEnd()}
1794
1922
  `);
1795
1923
  }
1796
- const wsMemoryDir = resolve9(wsDir, "memory");
1924
+ const wsMemoryDir = resolve8(wsDir, "memory");
1797
1925
  let dailyLogCount = 0;
1798
- if (existsSync6(wsMemoryDir)) {
1799
- const destMemoryDir = resolve9(dest, "home/memory");
1800
- const files = readdirSync4(wsMemoryDir).filter((f) => f.endsWith(".md"));
1926
+ if (existsSync5(wsMemoryDir)) {
1927
+ const destMemoryDir = resolve8(dest, "home/memory");
1928
+ const files = readdirSync3(wsMemoryDir).filter((f) => f.endsWith(".md"));
1801
1929
  for (const file of files) {
1802
- cpSync2(resolve9(wsMemoryDir, file), resolve9(destMemoryDir, file));
1930
+ cpSync(resolve8(wsMemoryDir, file), resolve8(destMemoryDir, file));
1803
1931
  }
1804
1932
  dailyLogCount = files.length;
1805
1933
  }
1806
1934
  const port = nextPort();
1807
1935
  addAgent(name, port);
1936
+ const homeDir = resolve8(dest, "home");
1808
1937
  ensureVoluteGroup();
1809
- createAgentUser(name);
1938
+ createAgentUser(name, homeDir);
1810
1939
  chownAgentDir(dest, name);
1811
- const ids = isIsolationEnabled() ? await getAgentUserIds(name) : void 0;
1812
- const env = ids ? { ...process.env, HOME: dest } : void 0;
1813
- await exec("npm", ["install"], { cwd: dest, uid: ids?.uid, gid: ids?.gid, env });
1940
+ const agentName = isIsolationEnabled() ? name : void 0;
1941
+ const env = agentName ? { ...process.env, HOME: homeDir } : void 0;
1942
+ await exec("npm", ["install"], { cwd: dest, agentName, env });
1814
1943
  if (!hasMemory && dailyLogCount > 0) {
1815
1944
  await consolidateMemory(dest);
1816
1945
  }
1817
- await gitExec(["init"], { cwd: dest, uid: ids?.uid, gid: ids?.gid, env });
1818
- await gitExec(["add", "-A"], { cwd: dest, uid: ids?.uid, gid: ids?.gid, env });
1819
- await gitExec(["commit", "-m", "import from OpenClaw"], {
1820
- cwd: dest,
1821
- uid: ids?.uid,
1822
- gid: ids?.gid,
1823
- env
1824
- });
1825
- const sessionFile = body.sessionPath ? resolve9(body.sessionPath) : findOpenClawSession(wsDir);
1826
- if (sessionFile && existsSync6(sessionFile)) {
1946
+ await gitExec(["init"], { cwd: dest, agentName, env });
1947
+ await gitExec(["add", "-A"], { cwd: dest, agentName, env });
1948
+ await gitExec(["commit", "-m", "import from OpenClaw"], { cwd: dest, agentName, env });
1949
+ const sessionFile = body.sessionPath ? resolve8(body.sessionPath) : findOpenClawSession(wsDir);
1950
+ if (sessionFile && existsSync5(sessionFile)) {
1827
1951
  if (template === "pi") {
1828
1952
  importPiSession(sessionFile, dest);
1829
1953
  } else if (template === "agent-sdk") {
1830
1954
  const sessionId = convertSession({ sessionPath: sessionFile, projectDir: dest });
1831
- const voluteDir = resolve9(dest, ".volute");
1832
- mkdirSync5(voluteDir, { recursive: true });
1833
- writeFileSync5(resolve9(voluteDir, "session.json"), JSON.stringify({ sessionId }));
1955
+ const voluteDir = resolve8(dest, ".volute");
1956
+ mkdirSync4(voluteDir, { recursive: true });
1957
+ writeFileSync4(resolve8(voluteDir, "session.json"), JSON.stringify({ sessionId }));
1834
1958
  }
1835
1959
  }
1836
1960
  importOpenClawConnectors(name, dest);
1837
1961
  return c.json({ ok: true, name, port, message: `Imported agent: ${name} (port ${port})` });
1838
1962
  } catch (err) {
1839
- if (existsSync6(dest)) rmSync2(dest, { recursive: true, force: true });
1963
+ if (existsSync5(dest)) rmSync(dest, { recursive: true, force: true });
1840
1964
  try {
1841
1965
  removeAgent(name);
1842
1966
  } catch {
1843
1967
  }
1844
1968
  return c.json({ error: err instanceof Error ? err.message : "Failed to import agent" }, 500);
1845
1969
  } finally {
1846
- rmSync2(composedDir, { recursive: true, force: true });
1970
+ rmSync(composedDir, { recursive: true, force: true });
1847
1971
  }
1848
1972
  }).get("/", async (c) => {
1849
1973
  const entries = readRegistry();
@@ -1858,7 +1982,7 @@ ${user.trimEnd()}
1858
1982
  const name = c.req.param("name");
1859
1983
  const entry = findAgent(name);
1860
1984
  if (!entry) return c.json({ error: "Agent not found" }, 404);
1861
- if (!existsSync6(agentDir(name))) return c.json({ error: "Agent directory missing" }, 404);
1985
+ if (!existsSync5(agentDir(name))) return c.json({ error: "Agent directory missing" }, 404);
1862
1986
  const { status, channels } = await getAgentStatus(name, entry.port);
1863
1987
  const variants = readVariants(name);
1864
1988
  const manager = getAgentManager();
@@ -1884,7 +2008,7 @@ ${user.trimEnd()}
1884
2008
  if (!variant) return c.json({ error: `Unknown variant: ${variantName}` }, 404);
1885
2009
  } else {
1886
2010
  const dir = agentDir(baseName);
1887
- if (!existsSync6(dir)) return c.json({ error: "Agent directory missing" }, 404);
2011
+ if (!existsSync5(dir)) return c.json({ error: "Agent directory missing" }, 404);
1888
2012
  }
1889
2013
  if (getAgentManager().isRunning(name)) {
1890
2014
  return c.json({ error: "Agent already running" }, 409);
@@ -1905,7 +2029,7 @@ ${user.trimEnd()}
1905
2029
  if (!variant) return c.json({ error: `Unknown variant: ${variantName}` }, 404);
1906
2030
  } else {
1907
2031
  const dir = agentDir(baseName);
1908
- if (!existsSync6(dir)) return c.json({ error: "Agent directory missing" }, 404);
2032
+ if (!existsSync5(dir)) return c.json({ error: "Agent directory missing" }, 404);
1909
2033
  }
1910
2034
  let context;
1911
2035
  const contentType = c.req.header("content-type");
@@ -1936,7 +2060,7 @@ ${user.trimEnd()}
1936
2060
  const variant = findVariant(baseName, mergeVariantName);
1937
2061
  if (variant) {
1938
2062
  const projectRoot = agentDir(baseName);
1939
- if (existsSync6(variant.path)) {
2063
+ if (existsSync5(variant.path)) {
1940
2064
  const status = (await gitExec(["status", "--porcelain"], { cwd: variant.path })).trim();
1941
2065
  if (status) {
1942
2066
  try {
@@ -1964,7 +2088,7 @@ ${user.trimEnd()}
1964
2088
  }
1965
2089
  }
1966
2090
  await gitExec(["merge", variant.branch], { cwd: projectRoot });
1967
- if (existsSync6(variant.path)) {
2091
+ if (existsSync5(variant.path)) {
1968
2092
  try {
1969
2093
  await gitExec(["worktree", "remove", "--force", variant.path], {
1970
2094
  cwd: projectRoot
@@ -1988,6 +2112,19 @@ ${user.trimEnd()}
1988
2112
  if (context) {
1989
2113
  manager.setPendingContext(name, context);
1990
2114
  }
2115
+ if (context?.type === "sprouted" && !variantName) {
2116
+ try {
2117
+ const db2 = await getDb();
2118
+ const activeConvs = await db2.select({ id: conversations.id }).from(conversations).where(eq4(conversations.agent_name, baseName)).all();
2119
+ for (const conv of activeConvs) {
2120
+ await addMessage(conv.id, "assistant", "system", [
2121
+ { type: "text", text: "[seed has sprouted]" }
2122
+ ]);
2123
+ }
2124
+ } catch (err) {
2125
+ console.error(`[daemon] failed to inject sprouted message for ${baseName}:`, err);
2126
+ }
2127
+ }
1991
2128
  await startAgentFull(name, baseName, variantName);
1992
2129
  return c.json({ ok: true });
1993
2130
  } catch (err) {
@@ -2033,11 +2170,11 @@ ${user.trimEnd()}
2033
2170
  removeAgent(name);
2034
2171
  await deleteAgentUser2(name);
2035
2172
  const state = stateDir(name);
2036
- if (existsSync6(state)) {
2037
- rmSync2(state, { recursive: true, force: true });
2173
+ if (existsSync5(state)) {
2174
+ rmSync(state, { recursive: true, force: true });
2038
2175
  }
2039
- if (force && existsSync6(dir)) {
2040
- rmSync2(dir, { recursive: true, force: true });
2176
+ if (force && existsSync5(dir)) {
2177
+ rmSync(dir, { recursive: true, force: true });
2041
2178
  deleteAgentUser(name);
2042
2179
  }
2043
2180
  return c.json({ ok: true });
@@ -2046,7 +2183,7 @@ ${user.trimEnd()}
2046
2183
  const entry = findAgent(agentName);
2047
2184
  if (!entry) return c.json({ error: "Agent not found" }, 404);
2048
2185
  const dir = agentDir(agentName);
2049
- if (!existsSync6(dir)) return c.json({ error: "Agent directory missing" }, 404);
2186
+ if (!existsSync5(dir)) return c.json({ error: "Agent directory missing" }, 404);
2050
2187
  let body = {};
2051
2188
  try {
2052
2189
  body = await c.req.json();
@@ -2055,8 +2192,8 @@ ${user.trimEnd()}
2055
2192
  const template = body.template ?? "agent-sdk";
2056
2193
  const UPGRADE_VARIANT = "upgrade";
2057
2194
  if (body.continue) {
2058
- const worktreeDir2 = resolve9(dir, ".variants", UPGRADE_VARIANT);
2059
- if (!existsSync6(worktreeDir2)) {
2195
+ const worktreeDir2 = resolve8(dir, ".variants", UPGRADE_VARIANT);
2196
+ if (!existsSync5(worktreeDir2)) {
2060
2197
  return c.json({ error: "No upgrade in progress" }, 400);
2061
2198
  }
2062
2199
  const status = await gitExec(["status", "--porcelain"], { cwd: worktreeDir2 });
@@ -2116,8 +2253,8 @@ ${user.trimEnd()}
2116
2253
  );
2117
2254
  }
2118
2255
  }
2119
- const worktreeDir = resolve9(dir, ".variants", UPGRADE_VARIANT);
2120
- if (existsSync6(worktreeDir)) {
2256
+ const worktreeDir = resolve8(dir, ".variants", UPGRADE_VARIANT);
2257
+ if (existsSync5(worktreeDir)) {
2121
2258
  return c.json(
2122
2259
  { error: "Upgrade variant already exists. Use continue or delete it first." },
2123
2260
  409
@@ -2129,9 +2266,9 @@ ${user.trimEnd()}
2129
2266
  } catch {
2130
2267
  }
2131
2268
  await updateTemplateBranch(dir, template, agentName);
2132
- const parentDir = resolve9(dir, ".variants");
2133
- if (!existsSync6(parentDir)) {
2134
- mkdirSync5(parentDir, { recursive: true });
2269
+ const parentDir = resolve8(dir, ".variants");
2270
+ if (!existsSync5(parentDir)) {
2271
+ mkdirSync4(parentDir, { recursive: true });
2135
2272
  }
2136
2273
  await gitExec(["worktree", "add", "-b", UPGRADE_VARIANT, worktreeDir], { cwd: dir });
2137
2274
  const hasConflicts = await mergeTemplateBranch(worktreeDir);
@@ -2258,6 +2395,24 @@ ${user.trimEnd()}
2258
2395
  budget.acknowledgeWarning(baseName);
2259
2396
  forwardBody = JSON.stringify(parsed);
2260
2397
  }
2398
+ const seedEntry = findAgent(baseName);
2399
+ if (seedEntry?.stage === "seed" && parsed) {
2400
+ try {
2401
+ const countResult = await db2.select({ count: sql3`count(*)` }).from(agentMessages).where(eq4(agentMessages.agent, baseName));
2402
+ const msgCount = countResult[0]?.count ?? 0;
2403
+ if (msgCount >= 10 && msgCount % 10 === 0) {
2404
+ const nudge = "\n[You've been exploring for a while. Whenever you feel ready, write your SOUL.md and MEMORY.md, then run volute sprout.]";
2405
+ if (typeof parsed.content === "string") {
2406
+ parsed.content = parsed.content + nudge;
2407
+ } else if (Array.isArray(parsed.content)) {
2408
+ parsed.content = [...parsed.content, { type: "text", text: nudge }];
2409
+ }
2410
+ forwardBody = JSON.stringify(parsed);
2411
+ }
2412
+ } catch (err) {
2413
+ console.error(`[daemon] failed to check seed message count for ${baseName}:`, err);
2414
+ }
2415
+ }
2261
2416
  typingMap.set(channel, baseName, { persistent: true });
2262
2417
  const conversationId = parsed?.conversationId ?? null;
2263
2418
  if (conversationId) typingMap.set(`volute:${conversationId}`, baseName, { persistent: true });
@@ -2324,7 +2479,7 @@ ${user.trimEnd()}
2324
2479
  }).get("/:name/history/channels", async (c) => {
2325
2480
  const name = c.req.param("name");
2326
2481
  const db2 = await getDb();
2327
- const rows = await db2.selectDistinct({ channel: agentMessages.channel }).from(agentMessages).where(eq3(agentMessages.agent, name));
2482
+ const rows = await db2.selectDistinct({ channel: agentMessages.channel }).from(agentMessages).where(eq4(agentMessages.agent, name));
2328
2483
  return c.json(rows.map((r) => r.channel));
2329
2484
  }).get("/:name/history", async (c) => {
2330
2485
  const name = c.req.param("name");
@@ -2332,23 +2487,23 @@ ${user.trimEnd()}
2332
2487
  const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
2333
2488
  const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
2334
2489
  const db2 = await getDb();
2335
- const conditions = [eq3(agentMessages.agent, name)];
2490
+ const conditions = [eq4(agentMessages.agent, name)];
2336
2491
  if (channel) {
2337
- conditions.push(eq3(agentMessages.channel, channel));
2492
+ conditions.push(eq4(agentMessages.channel, channel));
2338
2493
  }
2339
- const rows = await db2.select().from(agentMessages).where(and2(...conditions)).orderBy(desc(agentMessages.created_at)).limit(limit).offset(offset);
2494
+ const rows = await db2.select().from(agentMessages).where(and3(...conditions)).orderBy(desc2(agentMessages.created_at)).limit(limit).offset(offset);
2340
2495
  return c.json(rows);
2341
2496
  });
2342
2497
  var agents_default = app;
2343
2498
 
2344
2499
  // src/web/routes/auth.ts
2345
- import { zValidator } from "@hono/zod-validator";
2500
+ import { zValidator as zValidator2 } from "@hono/zod-validator";
2346
2501
  import { Hono as Hono2 } from "hono";
2347
2502
  import { deleteCookie, getCookie as getCookie2, setCookie } from "hono/cookie";
2348
- import { z } from "zod";
2349
- var credentialsSchema = z.object({
2350
- username: z.string().min(1),
2351
- password: z.string().min(1)
2503
+ import { z as z2 } from "zod";
2504
+ var credentialsSchema = z2.object({
2505
+ username: z2.string().min(1),
2506
+ password: z2.string().min(1)
2352
2507
  });
2353
2508
  var admin = new Hono2().use(authMiddleware).get("/users", async (c) => {
2354
2509
  const user = c.get("user");
@@ -2373,7 +2528,7 @@ var admin = new Hono2().use(authMiddleware).get("/users", async (c) => {
2373
2528
  await approveUser(id);
2374
2529
  return c.json({ ok: true });
2375
2530
  });
2376
- var app2 = new Hono2().post("/register", zValidator("json", credentialsSchema), async (c) => {
2531
+ var app2 = new Hono2().post("/register", zValidator2("json", credentialsSchema), async (c) => {
2377
2532
  const { username, password } = c.req.valid("json");
2378
2533
  const existing = await getUserByUsername(username);
2379
2534
  if (existing) {
@@ -2385,7 +2540,7 @@ var app2 = new Hono2().post("/register", zValidator("json", credentialsSchema),
2385
2540
  setCookie(c, "volute_session", sessionId, { path: "/", httpOnly: true, sameSite: "Lax" });
2386
2541
  }
2387
2542
  return c.json({ id: user.id, username: user.username, role: user.role });
2388
- }).post("/login", zValidator("json", credentialsSchema), async (c) => {
2543
+ }).post("/login", zValidator2("json", credentialsSchema), async (c) => {
2389
2544
  const { username, password } = c.req.valid("json");
2390
2545
  const user = await verifyUser(username, password);
2391
2546
  if (!user) {
@@ -2534,6 +2689,8 @@ var app4 = new Hono4().get("/:name/connectors", (c) => {
2534
2689
  }
2535
2690
  const entry = findAgent(name);
2536
2691
  if (!entry) return c.json({ error: "Agent not found" }, 404);
2692
+ if (entry.stage === "seed")
2693
+ return c.json({ error: "Seed agents cannot use connectors \u2014 sprout first" }, 403);
2537
2694
  const dir = agentDir(name);
2538
2695
  const manager = getConnectorManager();
2539
2696
  const envCheck = manager.checkConnectorEnv(type, name, dir);
@@ -2655,9 +2812,9 @@ var sharedEnvApp = new Hono5().get("/", (c) => {
2655
2812
  var env_default = app5;
2656
2813
 
2657
2814
  // src/web/routes/files.ts
2658
- import { existsSync as existsSync7 } from "fs";
2815
+ import { existsSync as existsSync6 } from "fs";
2659
2816
  import { readdir, readFile } from "fs/promises";
2660
- import { resolve as resolve10 } from "path";
2817
+ import { resolve as resolve9 } from "path";
2661
2818
  import { Hono as Hono6 } from "hono";
2662
2819
  var ALLOWED_FILES = /* @__PURE__ */ new Set(["SOUL.md", "MEMORY.md", "CLAUDE.md", "VOLUTE.md"]);
2663
2820
  var app6 = new Hono6().get("/:name/files", async (c) => {
@@ -2665,8 +2822,8 @@ var app6 = new Hono6().get("/:name/files", async (c) => {
2665
2822
  const entry = findAgent(name);
2666
2823
  if (!entry) return c.json({ error: "Agent not found" }, 404);
2667
2824
  const dir = agentDir(name);
2668
- const homeDir = resolve10(dir, "home");
2669
- if (!existsSync7(homeDir)) return c.json({ error: "Home directory missing" }, 404);
2825
+ const homeDir = resolve9(dir, "home");
2826
+ if (!existsSync6(homeDir)) return c.json({ error: "Home directory missing" }, 404);
2670
2827
  const allFiles = await readdir(homeDir);
2671
2828
  const files = allFiles.filter((f) => f.endsWith(".md") && ALLOWED_FILES.has(f));
2672
2829
  return c.json(files);
@@ -2679,8 +2836,8 @@ var app6 = new Hono6().get("/:name/files", async (c) => {
2679
2836
  const entry = findAgent(name);
2680
2837
  if (!entry) return c.json({ error: "Agent not found" }, 404);
2681
2838
  const dir = agentDir(name);
2682
- const filePath = resolve10(dir, "home", filename);
2683
- if (!existsSync7(filePath)) {
2839
+ const filePath = resolve9(dir, "home", filename);
2840
+ if (!existsSync6(filePath)) {
2684
2841
  return c.json({ error: "File not found" }, 404);
2685
2842
  }
2686
2843
  const content = await readFile(filePath, "utf-8");
@@ -2690,16 +2847,16 @@ var files_default = app6;
2690
2847
 
2691
2848
  // src/web/routes/logs.ts
2692
2849
  import { spawn as spawn2 } from "child_process";
2693
- import { existsSync as existsSync8 } from "fs";
2694
- import { resolve as resolve11 } from "path";
2850
+ import { existsSync as existsSync7 } from "fs";
2851
+ import { resolve as resolve10 } from "path";
2695
2852
  import { Hono as Hono7 } from "hono";
2696
2853
  import { streamSSE } from "hono/streaming";
2697
2854
  var app7 = new Hono7().get("/:name/logs", async (c) => {
2698
2855
  const name = c.req.param("name");
2699
2856
  const entry = findAgent(name);
2700
2857
  if (!entry) return c.json({ error: "Agent not found" }, 404);
2701
- const logFile = resolve11(stateDir(name), "logs", "agent.log");
2702
- if (!existsSync8(logFile)) {
2858
+ const logFile = resolve10(stateDir(name), "logs", "agent.log");
2859
+ if (!existsSync7(logFile)) {
2703
2860
  return c.json({ error: "No log file found" }, 404);
2704
2861
  }
2705
2862
  return streamSSE(c, async (stream) => {
@@ -2717,17 +2874,17 @@ var app7 = new Hono7().get("/:name/logs", async (c) => {
2717
2874
  stream.onAbort(() => {
2718
2875
  tail.kill();
2719
2876
  });
2720
- await new Promise((resolve17) => {
2721
- tail.on("exit", resolve17);
2722
- stream.onAbort(resolve17);
2877
+ await new Promise((resolve16) => {
2878
+ tail.on("exit", resolve16);
2879
+ stream.onAbort(resolve16);
2723
2880
  });
2724
2881
  });
2725
2882
  }).get("/:name/logs/tail", async (c) => {
2726
2883
  const name = c.req.param("name");
2727
2884
  const entry = findAgent(name);
2728
2885
  if (!entry) return c.json({ error: "Agent not found" }, 404);
2729
- const logFile = resolve11(stateDir(name), "logs", "agent.log");
2730
- if (!existsSync8(logFile)) {
2886
+ const logFile = resolve10(stateDir(name), "logs", "agent.log");
2887
+ if (!existsSync7(logFile)) {
2731
2888
  return c.json({ error: "No log file found" }, 404);
2732
2889
  }
2733
2890
  const nParam = parseInt(c.req.query("n") ?? "50", 10);
@@ -2737,8 +2894,8 @@ var app7 = new Hono7().get("/:name/logs", async (c) => {
2737
2894
  tail.stdout.on("data", (data) => {
2738
2895
  output += data.toString();
2739
2896
  });
2740
- await new Promise((resolve17) => {
2741
- tail.on("exit", resolve17);
2897
+ await new Promise((resolve16) => {
2898
+ tail.on("exit", resolve16);
2742
2899
  });
2743
2900
  return c.text(output);
2744
2901
  });
@@ -2762,7 +2919,10 @@ var app8 = new Hono8().get("/:name/schedules", (c) => {
2762
2919
  return c.json(readSchedules(name));
2763
2920
  }).post("/:name/schedules", requireAdmin, async (c) => {
2764
2921
  const name = c.req.param("name");
2765
- if (!findAgent(name)) return c.json({ error: "Agent not found" }, 404);
2922
+ const entry = findAgent(name);
2923
+ if (!entry) return c.json({ error: "Agent not found" }, 404);
2924
+ if (entry.stage === "seed")
2925
+ return c.json({ error: "Seed agents cannot use schedules \u2014 sprout first" }, 403);
2766
2926
  const body = await c.req.json();
2767
2927
  if (!body.cron || !body.message) {
2768
2928
  return c.json({ error: "cron and message are required" }, 400);
@@ -2846,10 +3006,10 @@ var app9 = new Hono9().post("/restart", requireAdmin, (c) => {
2846
3006
  stream.writeSSE({ data: JSON.stringify(entry) }).catch(() => {
2847
3007
  });
2848
3008
  });
2849
- await new Promise((resolve17) => {
3009
+ await new Promise((resolve16) => {
2850
3010
  stream.onAbort(() => {
2851
3011
  unsubscribe();
2852
- resolve17();
3012
+ resolve16();
2853
3013
  });
2854
3014
  });
2855
3015
  });
@@ -2857,15 +3017,15 @@ var app9 = new Hono9().post("/restart", requireAdmin, (c) => {
2857
3017
  var system_default = app9;
2858
3018
 
2859
3019
  // src/web/routes/typing.ts
2860
- import { zValidator as zValidator2 } from "@hono/zod-validator";
3020
+ import { zValidator as zValidator3 } from "@hono/zod-validator";
2861
3021
  import { Hono as Hono10 } from "hono";
2862
- import { z as z2 } from "zod";
2863
- var typingSchema = z2.object({
2864
- channel: z2.string().min(1),
2865
- sender: z2.string().min(1),
2866
- active: z2.boolean()
3022
+ import { z as z3 } from "zod";
3023
+ var typingSchema = z3.object({
3024
+ channel: z3.string().min(1),
3025
+ sender: z3.string().min(1),
3026
+ active: z3.boolean()
2867
3027
  });
2868
- var app10 = new Hono10().post("/:name/typing", zValidator2("json", typingSchema), (c) => {
3028
+ var app10 = new Hono10().post("/:name/typing", zValidator3("json", typingSchema), (c) => {
2869
3029
  const { channel, sender, active } = c.req.valid("json");
2870
3030
  const map = getTypingMap();
2871
3031
  if (active) {
@@ -2906,16 +3066,16 @@ var app11 = new Hono11().get("/update", async (c) => {
2906
3066
  var update_default = app11;
2907
3067
 
2908
3068
  // src/web/routes/variants.ts
2909
- import { existsSync as existsSync9, mkdirSync as mkdirSync7, writeFileSync as writeFileSync6 } from "fs";
2910
- import { resolve as resolve13 } from "path";
3069
+ import { existsSync as existsSync8, mkdirSync as mkdirSync6, writeFileSync as writeFileSync5 } from "fs";
3070
+ import { resolve as resolve12 } from "path";
2911
3071
  import { Hono as Hono12 } from "hono";
2912
3072
 
2913
3073
  // src/lib/spawn-server.ts
2914
3074
  import { spawn as spawn4 } from "child_process";
2915
- import { closeSync, mkdirSync as mkdirSync6, openSync, readFileSync as readFileSync7 } from "fs";
2916
- import { resolve as resolve12 } from "path";
3075
+ import { closeSync, mkdirSync as mkdirSync5, openSync, readFileSync as readFileSync6 } from "fs";
3076
+ import { resolve as resolve11 } from "path";
2917
3077
  function tsxBin(cwd) {
2918
- return resolve12(cwd, "node_modules", ".bin", "tsx");
3078
+ return resolve11(cwd, "node_modules", ".bin", "tsx");
2919
3079
  }
2920
3080
  function spawnServer(cwd, port, options) {
2921
3081
  if (options?.detached) {
@@ -2928,31 +3088,31 @@ function spawnAttached(cwd, port) {
2928
3088
  cwd,
2929
3089
  stdio: ["ignore", "pipe", "pipe"]
2930
3090
  });
2931
- return new Promise((resolve17) => {
2932
- const timeout = setTimeout(() => resolve17(null), 3e4);
3091
+ return new Promise((resolve16) => {
3092
+ const timeout = setTimeout(() => resolve16(null), 3e4);
2933
3093
  function checkOutput(data) {
2934
3094
  const match = data.toString().match(/listening on :(\d+)/);
2935
3095
  if (match) {
2936
3096
  clearTimeout(timeout);
2937
- resolve17({ child, actualPort: parseInt(match[1], 10) });
3097
+ resolve16({ child, actualPort: parseInt(match[1], 10) });
2938
3098
  }
2939
3099
  }
2940
3100
  child.stdout?.on("data", checkOutput);
2941
3101
  child.stderr?.on("data", checkOutput);
2942
3102
  child.on("error", () => {
2943
3103
  clearTimeout(timeout);
2944
- resolve17(null);
3104
+ resolve16(null);
2945
3105
  });
2946
3106
  child.on("exit", () => {
2947
3107
  clearTimeout(timeout);
2948
- resolve17(null);
3108
+ resolve16(null);
2949
3109
  });
2950
3110
  });
2951
3111
  }
2952
3112
  function spawnDetached(cwd, port, logDir) {
2953
- const logsDir = logDir ?? resolve12(cwd, ".volute", "logs");
2954
- mkdirSync6(logsDir, { recursive: true });
2955
- const logPath = resolve12(logsDir, "agent.log");
3113
+ const logsDir = logDir ?? resolve11(cwd, ".volute", "logs");
3114
+ mkdirSync5(logsDir, { recursive: true });
3115
+ const logPath = resolve11(logsDir, "agent.log");
2956
3116
  const logFd = openSync(logPath, "a");
2957
3117
  const child = spawn4(tsxBin(cwd), ["src/server.ts", "--port", String(port)], {
2958
3118
  cwd,
@@ -2972,7 +3132,7 @@ function spawnDetached(cwd, port, logDir) {
2972
3132
  }
2973
3133
  const interval = setInterval(() => {
2974
3134
  try {
2975
- const content = readFileSync7(logPath, "utf-8");
3135
+ const content = readFileSync6(logPath, "utf-8");
2976
3136
  const match = content.match(/listening on :(\d+)/);
2977
3137
  if (match) {
2978
3138
  finish({ child, actualPort: parseInt(match[1], 10) });
@@ -3041,6 +3201,8 @@ var app12 = new Hono12().get("/:name/variants", async (c) => {
3041
3201
  const agentName = c.req.param("name");
3042
3202
  const entry = findAgent(agentName);
3043
3203
  if (!entry) return c.json({ error: "Agent not found" }, 404);
3204
+ if (entry.stage === "seed")
3205
+ return c.json({ error: "Seed agents cannot create variants \u2014 sprout first" }, 403);
3044
3206
  let body;
3045
3207
  try {
3046
3208
  body = await c.req.json();
@@ -3052,11 +3214,11 @@ var app12 = new Hono12().get("/:name/variants", async (c) => {
3052
3214
  const err = validateBranchName(variantName);
3053
3215
  if (err) return c.json({ error: err }, 400);
3054
3216
  const projectRoot = agentDir(agentName);
3055
- const variantDir = resolve13(projectRoot, ".variants", variantName);
3056
- if (existsSync9(variantDir)) {
3217
+ const variantDir = resolve12(projectRoot, ".variants", variantName);
3218
+ if (existsSync8(variantDir)) {
3057
3219
  return c.json({ error: `Variant directory already exists: ${variantDir}` }, 409);
3058
3220
  }
3059
- mkdirSync7(resolve13(projectRoot, ".variants"), { recursive: true });
3221
+ mkdirSync6(resolve12(projectRoot, ".variants"), { recursive: true });
3060
3222
  try {
3061
3223
  await gitExec(["worktree", "add", "-b", variantName, variantDir], { cwd: projectRoot });
3062
3224
  } catch (e) {
@@ -3070,7 +3232,7 @@ var app12 = new Hono12().get("/:name/variants", async (c) => {
3070
3232
  return c.json({ error: `npm install failed: ${msg}` }, 500);
3071
3233
  }
3072
3234
  if (body.soul) {
3073
- writeFileSync6(resolve13(variantDir, "home/SOUL.md"), body.soul);
3235
+ writeFileSync5(resolve12(variantDir, "home/SOUL.md"), body.soul);
3074
3236
  }
3075
3237
  const variantPort = body.port ?? nextPort();
3076
3238
  const variant = {
@@ -3109,7 +3271,7 @@ var app12 = new Hono12().get("/:name/variants", async (c) => {
3109
3271
  } catch {
3110
3272
  }
3111
3273
  const projectRoot = agentDir(agentName);
3112
- if (existsSync9(variant.path)) {
3274
+ if (existsSync8(variant.path)) {
3113
3275
  const status = (await gitExec(["status", "--porcelain"], { cwd: variant.path })).trim();
3114
3276
  if (status) {
3115
3277
  try {
@@ -3166,7 +3328,7 @@ var app12 = new Hono12().get("/:name/variants", async (c) => {
3166
3328
  } catch (e) {
3167
3329
  return c.json({ error: "Merge failed. Resolve conflicts manually." }, 500);
3168
3330
  }
3169
- if (existsSync9(variant.path)) {
3331
+ if (existsSync8(variant.path)) {
3170
3332
  try {
3171
3333
  await gitExec(["worktree", "remove", "--force", variant.path], { cwd: projectRoot });
3172
3334
  } catch {
@@ -3218,7 +3380,7 @@ var app12 = new Hono12().get("/:name/variants", async (c) => {
3218
3380
  } catch {
3219
3381
  }
3220
3382
  }
3221
- if (existsSync9(variant.path)) {
3383
+ if (existsSync8(variant.path)) {
3222
3384
  try {
3223
3385
  await gitExec(["worktree", "remove", "--force", variant.path], { cwd: projectRoot });
3224
3386
  } catch {
@@ -3235,221 +3397,26 @@ var app12 = new Hono12().get("/:name/variants", async (c) => {
3235
3397
  var variants_default = app12;
3236
3398
 
3237
3399
  // src/web/routes/volute/chat.ts
3238
- import { readFileSync as readFileSync8 } from "fs";
3239
- import { resolve as resolve14 } from "path";
3240
- import { zValidator as zValidator3 } from "@hono/zod-validator";
3400
+ import { readFileSync as readFileSync7 } from "fs";
3401
+ import { resolve as resolve13 } from "path";
3402
+ import { zValidator as zValidator4 } from "@hono/zod-validator";
3241
3403
  import { Hono as Hono13 } from "hono";
3242
3404
  import { streamSSE as streamSSE3 } from "hono/streaming";
3243
- import { z as z3 } from "zod";
3244
-
3245
- // src/lib/conversation-events.ts
3246
- var subscribers = /* @__PURE__ */ new Map();
3247
- function subscribe(conversationId, callback) {
3248
- let set = subscribers.get(conversationId);
3249
- if (!set) {
3250
- set = /* @__PURE__ */ new Set();
3251
- subscribers.set(conversationId, set);
3252
- }
3253
- set.add(callback);
3254
- return () => {
3255
- set.delete(callback);
3256
- if (set.size === 0) subscribers.delete(conversationId);
3257
- };
3258
- }
3259
- function publish(conversationId, event) {
3260
- const set = subscribers.get(conversationId);
3261
- if (!set) return;
3262
- for (const cb of set) {
3263
- try {
3264
- cb(event);
3265
- } catch (err) {
3266
- console.error("[conversation-events] subscriber threw:", err);
3267
- set.delete(cb);
3268
- if (set.size === 0) subscribers.delete(conversationId);
3269
- }
3270
- }
3271
- }
3272
-
3273
- // src/lib/conversations.ts
3274
- import { randomUUID as randomUUID2 } from "crypto";
3275
- import { and as and3, desc as desc2, eq as eq4, inArray, isNull, sql as sql2 } from "drizzle-orm";
3276
- async function createConversation(agentName, channel, opts) {
3277
- const db2 = await getDb();
3278
- const id = randomUUID2();
3279
- await db2.insert(conversations).values({
3280
- id,
3281
- agent_name: agentName,
3282
- channel,
3283
- user_id: opts?.userId ?? null,
3284
- title: opts?.title ?? null
3285
- });
3286
- if (opts?.participantIds && opts.participantIds.length > 0) {
3287
- await db2.insert(conversationParticipants).values(
3288
- opts.participantIds.map((uid, i) => ({
3289
- conversation_id: id,
3290
- user_id: uid,
3291
- role: i === 0 ? "owner" : "member"
3292
- }))
3293
- );
3294
- }
3295
- return {
3296
- id,
3297
- agent_name: agentName,
3298
- channel,
3299
- user_id: opts?.userId ?? null,
3300
- title: opts?.title ?? null,
3301
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
3302
- updated_at: (/* @__PURE__ */ new Date()).toISOString()
3303
- };
3304
- }
3305
- async function getConversation(id) {
3306
- const db2 = await getDb();
3307
- const row = await db2.select().from(conversations).where(eq4(conversations.id, id)).get();
3308
- return row ?? null;
3309
- }
3310
- async function getParticipants(conversationId) {
3311
- const db2 = await getDb();
3312
- const rows = await db2.select({
3313
- userId: conversationParticipants.user_id,
3314
- username: users.username,
3315
- userType: users.user_type,
3316
- role: conversationParticipants.role
3317
- }).from(conversationParticipants).innerJoin(users, eq4(conversationParticipants.user_id, users.id)).where(eq4(conversationParticipants.conversation_id, conversationId)).all();
3318
- return rows;
3319
- }
3320
- async function isParticipant(conversationId, userId) {
3321
- const db2 = await getDb();
3322
- const row = await db2.select({ user_id: conversationParticipants.user_id }).from(conversationParticipants).where(
3323
- and3(
3324
- eq4(conversationParticipants.conversation_id, conversationId),
3325
- eq4(conversationParticipants.user_id, userId)
3326
- )
3327
- ).get();
3328
- return row != null;
3329
- }
3330
- async function listConversationsForUser(userId) {
3331
- const db2 = await getDb();
3332
- const participantRows = await db2.select({ conversation_id: conversationParticipants.conversation_id }).from(conversationParticipants).where(eq4(conversationParticipants.user_id, userId)).all();
3333
- if (participantRows.length === 0) return [];
3334
- const convIds = participantRows.map((r) => r.conversation_id);
3335
- return db2.select().from(conversations).where(inArray(conversations.id, convIds)).orderBy(desc2(conversations.updated_at)).all();
3336
- }
3337
- async function isParticipantOrOwner(conversationId, userId) {
3338
- if (await isParticipant(conversationId, userId)) return true;
3339
- const db2 = await getDb();
3340
- const row = await db2.select().from(conversations).where(and3(eq4(conversations.id, conversationId), eq4(conversations.user_id, userId))).get();
3341
- return row != null;
3342
- }
3343
- async function deleteConversationForUser(id, userId) {
3344
- if (!await isParticipantOrOwner(id, userId)) return false;
3345
- await deleteConversation(id);
3346
- return true;
3347
- }
3348
- async function addMessage(conversationId, role, senderName, content) {
3349
- const db2 = await getDb();
3350
- const serialized = JSON.stringify(content);
3351
- const [result] = await db2.insert(messages).values({ conversation_id: conversationId, role, sender_name: senderName, content: serialized }).returning({ id: messages.id, created_at: messages.created_at });
3352
- await db2.update(conversations).set({ updated_at: sql2`datetime('now')` }).where(eq4(conversations.id, conversationId));
3353
- if (role === "user") {
3354
- const firstText = content.find((b) => b.type === "text");
3355
- const title = firstText ? firstText.text.slice(0, 80) : "";
3356
- if (title) {
3357
- await db2.update(conversations).set({ title }).where(and3(eq4(conversations.id, conversationId), isNull(conversations.title)));
3358
- }
3359
- }
3360
- const msg = {
3361
- id: result.id,
3362
- conversation_id: conversationId,
3363
- role,
3364
- sender_name: senderName,
3365
- content,
3366
- created_at: result.created_at
3367
- };
3368
- publish(conversationId, {
3369
- type: "message",
3370
- id: msg.id,
3371
- role: msg.role,
3372
- senderName: msg.sender_name,
3373
- content: msg.content,
3374
- createdAt: msg.created_at
3375
- });
3376
- return msg;
3377
- }
3378
- async function getMessages(conversationId) {
3379
- const db2 = await getDb();
3380
- const rows = await db2.select().from(messages).where(eq4(messages.conversation_id, conversationId)).orderBy(messages.created_at).all();
3381
- return rows.map((row) => {
3382
- let content;
3383
- try {
3384
- const parsed = JSON.parse(row.content);
3385
- content = Array.isArray(parsed) ? parsed : [{ type: "text", text: row.content }];
3386
- } catch {
3387
- content = [{ type: "text", text: row.content }];
3388
- }
3389
- return { ...row, content };
3390
- });
3391
- }
3392
- async function listConversationsWithParticipants(userId) {
3393
- const convs = await listConversationsForUser(userId);
3394
- if (convs.length === 0) return [];
3395
- const db2 = await getDb();
3396
- const convIds = convs.map((c) => c.id);
3397
- const rows = await db2.select({
3398
- conversationId: conversationParticipants.conversation_id,
3399
- userId: users.id,
3400
- username: users.username,
3401
- userType: users.user_type,
3402
- role: conversationParticipants.role
3403
- }).from(conversationParticipants).innerJoin(users, eq4(conversationParticipants.user_id, users.id)).where(inArray(conversationParticipants.conversation_id, convIds));
3404
- const byConv = /* @__PURE__ */ new Map();
3405
- for (const r of rows) {
3406
- let arr = byConv.get(r.conversationId);
3407
- if (!arr) {
3408
- arr = [];
3409
- byConv.set(r.conversationId, arr);
3410
- }
3411
- arr.push({
3412
- userId: r.userId,
3413
- username: r.username,
3414
- userType: r.userType,
3415
- role: r.role
3416
- });
3417
- }
3418
- return convs.map((c) => ({ ...c, participants: byConv.get(c.id) ?? [] }));
3419
- }
3420
- async function findDMConversation(agentName, participantIds) {
3421
- const db2 = await getDb();
3422
- const agentConvs = await db2.select({ id: conversations.id }).from(conversations).where(eq4(conversations.agent_name, agentName)).all();
3423
- for (const conv of agentConvs) {
3424
- const rows = await db2.select({ user_id: conversationParticipants.user_id }).from(conversationParticipants).where(eq4(conversationParticipants.conversation_id, conv.id)).all();
3425
- if (rows.length !== 2) continue;
3426
- const ids = new Set(rows.map((r) => r.user_id));
3427
- if (ids.has(participantIds[0]) && ids.has(participantIds[1])) {
3428
- return conv.id;
3429
- }
3430
- }
3431
- return null;
3432
- }
3433
- async function deleteConversation(id) {
3434
- const db2 = await getDb();
3435
- await db2.delete(conversations).where(eq4(conversations.id, id));
3436
- }
3437
-
3438
- // src/web/routes/volute/chat.ts
3439
- var chatSchema = z3.object({
3440
- message: z3.string().optional(),
3441
- conversationId: z3.string().optional(),
3442
- sender: z3.string().optional(),
3443
- images: z3.array(
3444
- z3.object({
3445
- media_type: z3.string(),
3446
- data: z3.string()
3405
+ import { z as z4 } from "zod";
3406
+ var chatSchema = z4.object({
3407
+ message: z4.string().optional(),
3408
+ conversationId: z4.string().optional(),
3409
+ sender: z4.string().optional(),
3410
+ images: z4.array(
3411
+ z4.object({
3412
+ media_type: z4.string(),
3413
+ data: z4.string()
3447
3414
  })
3448
3415
  ).optional()
3449
3416
  });
3450
3417
  function getDaemonUrl() {
3451
3418
  try {
3452
- const data = JSON.parse(readFileSync8(resolve14(voluteHome(), "daemon.json"), "utf-8"));
3419
+ const data = JSON.parse(readFileSync7(resolve13(voluteHome(), "daemon.json"), "utf-8"));
3453
3420
  return `http://${daemonLoopback()}:${data.port}`;
3454
3421
  } catch (err) {
3455
3422
  throw new Error(`Failed to read daemon config: ${err instanceof Error ? err.message : err}`);
@@ -3465,7 +3432,7 @@ function daemonFetchInternal(path, body) {
3465
3432
  if (token) headers.Authorization = `Bearer ${token}`;
3466
3433
  return fetch(`${daemonUrl}${path}`, { method: "POST", headers, body });
3467
3434
  }
3468
- var app13 = new Hono13().post("/:name/chat", zValidator3("json", chatSchema), async (c) => {
3435
+ var app13 = new Hono13().post("/:name/chat", zValidator4("json", chatSchema), async (c) => {
3469
3436
  const name = c.req.param("name");
3470
3437
  const [baseName] = name.split("@", 2);
3471
3438
  const entry = findAgent(baseName);
@@ -3513,7 +3480,6 @@ var app13 = new Hono13().post("/:name/chat", zValidator3("json", chatSchema), as
3513
3480
  }
3514
3481
  const conv = await getConversation(conversationId);
3515
3482
  const convTitle = conv?.title;
3516
- const channel = convTitle ? `volute:${slugify(convTitle)}` : `volute:${conversationId}`;
3517
3483
  const contentBlocks = [];
3518
3484
  if (body.message) {
3519
3485
  contentBlocks.push({ type: "text", text: body.message });
@@ -3527,13 +3493,21 @@ var app13 = new Hono13().post("/:name/chat", zValidator3("json", chatSchema), as
3527
3493
  const participants = await getParticipants(conversationId);
3528
3494
  const agentParticipants = participants.filter((p) => p.userType === "agent");
3529
3495
  const participantNames = participants.map((p) => p.username);
3530
- const { getAgentManager: getAgentManager2 } = await import("./agent-manager-AZUDAKCP.js");
3496
+ const { getAgentManager: getAgentManager2 } = await import("./agent-manager-HHBAAL2D.js");
3531
3497
  const manager = getAgentManager2();
3532
3498
  const runningAgents = agentParticipants.map((ap) => {
3533
3499
  const agentKey = ap.username === baseName ? name : ap.username;
3534
3500
  return manager.isRunning(agentKey) ? ap.username : null;
3535
3501
  }).filter((n) => n !== null && n !== senderName);
3536
3502
  const isDM = participants.length === 2;
3503
+ function channelForAgent(agentUsername) {
3504
+ return buildVoluteSlug({
3505
+ participants,
3506
+ agentUsername,
3507
+ convTitle,
3508
+ conversationId
3509
+ });
3510
+ }
3537
3511
  const channelEntry = {
3538
3512
  platformId: conversationId,
3539
3513
  platform: "volute",
@@ -3542,25 +3516,26 @@ var app13 = new Hono13().post("/:name/chat", zValidator3("json", chatSchema), as
3542
3516
  };
3543
3517
  for (const ap of agentParticipants) {
3544
3518
  try {
3545
- writeChannelEntry(ap.username, channel, channelEntry);
3519
+ writeChannelEntry(ap.username, channelForAgent(ap.username), channelEntry);
3546
3520
  } catch (err) {
3547
3521
  console.warn(`[chat] failed to write channel entry for ${ap.username}:`, err);
3548
3522
  }
3549
3523
  }
3550
- const typingMap = getTypingMap();
3551
- const currentlyTyping = typingMap.get(channel);
3552
- const payload = JSON.stringify({
3553
- content: contentBlocks,
3554
- channel,
3555
- conversationId,
3556
- sender: senderName,
3557
- participants: participantNames,
3558
- participantCount: participants.length,
3559
- isDM,
3560
- ...currentlyTyping.length > 0 ? { typing: currentlyTyping } : {}
3561
- });
3562
3524
  for (const agentName of runningAgents) {
3563
3525
  const targetName = agentName === baseName ? name : agentName;
3526
+ const channel = channelForAgent(agentName);
3527
+ const typingMap = getTypingMap();
3528
+ const currentlyTyping = typingMap.get(channel);
3529
+ const payload = JSON.stringify({
3530
+ content: contentBlocks,
3531
+ channel,
3532
+ conversationId,
3533
+ sender: senderName,
3534
+ participants: participantNames,
3535
+ participantCount: participants.length,
3536
+ isDM,
3537
+ ...currentlyTyping.length > 0 ? { typing: currentlyTyping } : {}
3538
+ });
3564
3539
  daemonFetchInternal(`/api/agents/${encodeURIComponent(targetName)}/message`, payload).then(async (res) => {
3565
3540
  if (!res.ok) {
3566
3541
  const text2 = await res.text().catch(() => "");
@@ -3588,11 +3563,11 @@ var app13 = new Hono13().post("/:name/chat", zValidator3("json", chatSchema), as
3588
3563
  if (!stream.aborted) console.error("[chat] SSE ping error:", err);
3589
3564
  });
3590
3565
  }, 15e3);
3591
- await new Promise((resolve17) => {
3566
+ await new Promise((resolve16) => {
3592
3567
  stream.onAbort(() => {
3593
3568
  unsubscribe();
3594
3569
  clearInterval(keepAlive);
3595
- resolve17();
3570
+ resolve16();
3596
3571
  });
3597
3572
  });
3598
3573
  });
@@ -3600,13 +3575,13 @@ var app13 = new Hono13().post("/:name/chat", zValidator3("json", chatSchema), as
3600
3575
  var chat_default = app13;
3601
3576
 
3602
3577
  // src/web/routes/volute/conversations.ts
3603
- import { zValidator as zValidator4 } from "@hono/zod-validator";
3578
+ import { zValidator as zValidator5 } from "@hono/zod-validator";
3604
3579
  import { Hono as Hono14 } from "hono";
3605
- import { z as z4 } from "zod";
3606
- var createConvSchema = z4.object({
3607
- title: z4.string().optional(),
3608
- participantIds: z4.array(z4.number()).optional(),
3609
- participantNames: z4.array(z4.string()).optional()
3580
+ import { z as z5 } from "zod";
3581
+ var createConvSchema = z5.object({
3582
+ title: z5.string().optional(),
3583
+ participantIds: z5.array(z5.number()).optional(),
3584
+ participantNames: z5.array(z5.string()).optional()
3610
3585
  });
3611
3586
  var app14 = new Hono14().get("/:name/conversations", async (c) => {
3612
3587
  const name = c.req.param("name");
@@ -3619,7 +3594,7 @@ var app14 = new Hono14().get("/:name/conversations", async (c) => {
3619
3594
  const all = await listConversationsForUser(lookupId);
3620
3595
  const convs = all.filter((c2) => c2.agent_name === name);
3621
3596
  return c.json(convs);
3622
- }).post("/:name/conversations", zValidator4("json", createConvSchema), async (c) => {
3597
+ }).post("/:name/conversations", zValidator5("json", createConvSchema), async (c) => {
3623
3598
  const name = c.req.param("name");
3624
3599
  const user = c.get("user");
3625
3600
  const body = c.req.valid("json");
@@ -3696,12 +3671,12 @@ var app14 = new Hono14().get("/:name/conversations", async (c) => {
3696
3671
  var conversations_default = app14;
3697
3672
 
3698
3673
  // src/web/routes/volute/user-conversations.ts
3699
- import { zValidator as zValidator5 } from "@hono/zod-validator";
3674
+ import { zValidator as zValidator6 } from "@hono/zod-validator";
3700
3675
  import { Hono as Hono15 } from "hono";
3701
- import { z as z5 } from "zod";
3702
- var createSchema = z5.object({
3703
- title: z5.string().optional(),
3704
- participantNames: z5.array(z5.string()).min(1)
3676
+ import { z as z6 } from "zod";
3677
+ var createSchema = z6.object({
3678
+ title: z6.string().optional(),
3679
+ participantNames: z6.array(z6.string()).min(1)
3705
3680
  });
3706
3681
  var app15 = new Hono15().use("*", authMiddleware).get("/", async (c) => {
3707
3682
  const user = c.get("user");
@@ -3715,7 +3690,7 @@ var app15 = new Hono15().use("*", authMiddleware).get("/", async (c) => {
3715
3690
  }
3716
3691
  const msgs = await getMessages(id);
3717
3692
  return c.json(msgs);
3718
- }).post("/", zValidator5("json", createSchema), async (c) => {
3693
+ }).post("/", zValidator6("json", createSchema), async (c) => {
3719
3694
  const user = c.get("user");
3720
3695
  const body = c.req.valid("json");
3721
3696
  const participantIds = /* @__PURE__ */ new Set();
@@ -3820,20 +3795,20 @@ async function startServer({
3820
3795
  hostname = "127.0.0.1"
3821
3796
  }) {
3822
3797
  let assetsDir = "";
3823
- let searchDir = dirname4(new URL(import.meta.url).pathname);
3798
+ let searchDir = dirname3(new URL(import.meta.url).pathname);
3824
3799
  for (let i = 0; i < 5; i++) {
3825
- const candidate = resolve15(searchDir, "dist", "web-assets");
3826
- if (existsSync10(candidate)) {
3800
+ const candidate = resolve14(searchDir, "dist", "web-assets");
3801
+ if (existsSync9(candidate)) {
3827
3802
  assetsDir = candidate;
3828
3803
  break;
3829
3804
  }
3830
- searchDir = dirname4(searchDir);
3805
+ searchDir = dirname3(searchDir);
3831
3806
  }
3832
3807
  if (assetsDir) {
3833
3808
  app_default.get("*", async (c) => {
3834
3809
  const urlPath = new URL(c.req.url).pathname;
3835
3810
  if (urlPath.startsWith("/api/")) return c.notFound();
3836
- const filePath = resolve15(assetsDir, urlPath.slice(1));
3811
+ const filePath = resolve14(assetsDir, urlPath.slice(1));
3837
3812
  if (!filePath.startsWith(assetsDir)) return c.text("Forbidden", 403);
3838
3813
  const s = await stat(filePath).catch(() => null);
3839
3814
  if (s?.isFile()) {
@@ -3842,7 +3817,7 @@ async function startServer({
3842
3817
  const body = await readFile2(filePath);
3843
3818
  return c.body(body, 200, { "Content-Type": mime });
3844
3819
  }
3845
- const indexPath = resolve15(assetsDir, "index.html");
3820
+ const indexPath = resolve14(assetsDir, "index.html");
3846
3821
  const indexStat = await stat(indexPath).catch(() => null);
3847
3822
  if (indexStat?.isFile()) {
3848
3823
  const body = await readFile2(indexPath, "utf-8");
@@ -3852,10 +3827,10 @@ async function startServer({
3852
3827
  });
3853
3828
  }
3854
3829
  const server = serve({ fetch: app_default.fetch, port, hostname });
3855
- await new Promise((resolve17, reject) => {
3830
+ await new Promise((resolve16, reject) => {
3856
3831
  server.on("listening", () => {
3857
3832
  logger_default.info("Volute UI running", { hostname, port });
3858
- resolve17();
3833
+ resolve16();
3859
3834
  });
3860
3835
  server.on("error", (err) => {
3861
3836
  reject(err);
@@ -3866,14 +3841,14 @@ async function startServer({
3866
3841
 
3867
3842
  // src/daemon.ts
3868
3843
  if (!process.env.VOLUTE_HOME) {
3869
- process.env.VOLUTE_HOME = resolve16(homedir2(), ".volute");
3844
+ process.env.VOLUTE_HOME = resolve15(homedir2(), ".volute");
3870
3845
  }
3871
3846
  async function startDaemon(opts) {
3872
3847
  const { port, hostname } = opts;
3873
3848
  const myPid = String(process.pid);
3874
3849
  const home = voluteHome();
3875
3850
  if (!opts.foreground) {
3876
- const log2 = new RotatingLog(resolve16(home, "daemon.log"));
3851
+ const log2 = new RotatingLog(resolve15(home, "daemon.log"));
3877
3852
  const write2 = (...args) => log2.write(`${format(...args)}
3878
3853
  `);
3879
3854
  console.log = write2;
@@ -3881,9 +3856,9 @@ async function startDaemon(opts) {
3881
3856
  console.warn = write2;
3882
3857
  console.info = write2;
3883
3858
  }
3884
- const DAEMON_PID_PATH = resolve16(home, "daemon.pid");
3885
- const DAEMON_JSON_PATH = resolve16(home, "daemon.json");
3886
- mkdirSync8(home, { recursive: true });
3859
+ const DAEMON_PID_PATH = resolve15(home, "daemon.pid");
3860
+ const DAEMON_JSON_PATH = resolve15(home, "daemon.json");
3861
+ mkdirSync7(home, { recursive: true });
3887
3862
  const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes(32).toString("hex");
3888
3863
  process.env.VOLUTE_DAEMON_TOKEN = token;
3889
3864
  process.env.VOLUTE_DAEMON_PORT = String(port);
@@ -3899,8 +3874,8 @@ async function startDaemon(opts) {
3899
3874
  }
3900
3875
  throw err;
3901
3876
  }
3902
- writeFileSync7(DAEMON_PID_PATH, myPid, { mode: 420 });
3903
- writeFileSync7(DAEMON_JSON_PATH, `${JSON.stringify({ port, hostname, token }, null, 2)}
3877
+ writeFileSync6(DAEMON_PID_PATH, myPid, { mode: 420 });
3878
+ writeFileSync6(DAEMON_JSON_PATH, `${JSON.stringify({ port, hostname, token }, null, 2)}
3904
3879
  `, {
3905
3880
  mode: 420
3906
3881
  });
@@ -3923,6 +3898,7 @@ async function startDaemon(opts) {
3923
3898
  if (!entry.running) continue;
3924
3899
  try {
3925
3900
  await manager.startAgent(entry.name);
3901
+ if (entry.stage === "seed") continue;
3926
3902
  const dir = agentDir(entry.name);
3927
3903
  await connectors.startConnectors(entry.name, dir, entry.port, port);
3928
3904
  scheduler.loadSchedules(entry.name);
@@ -3954,13 +3930,13 @@ async function startDaemon(opts) {
3954
3930
  console.error(`[daemon] running on ${hostname}:${port}, pid ${myPid}`);
3955
3931
  function cleanup() {
3956
3932
  try {
3957
- if (readFileSync9(DAEMON_PID_PATH, "utf-8").trim() === myPid) {
3933
+ if (readFileSync8(DAEMON_PID_PATH, "utf-8").trim() === myPid) {
3958
3934
  unlinkSync2(DAEMON_PID_PATH);
3959
3935
  }
3960
3936
  } catch {
3961
3937
  }
3962
3938
  try {
3963
- const data = JSON.parse(readFileSync9(DAEMON_JSON_PATH, "utf-8"));
3939
+ const data = JSON.parse(readFileSync8(DAEMON_JSON_PATH, "utf-8"));
3964
3940
  if (data.token === token) {
3965
3941
  unlinkSync2(DAEMON_JSON_PATH);
3966
3942
  }