volute 0.26.0 → 0.28.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 (141) hide show
  1. package/README.md +18 -18
  2. package/dist/accept-666DIZX2.js +41 -0
  3. package/dist/{activity-events-ZMBAKLUF.js → activity-events-BBIEA2F4.js} +2 -3
  4. package/dist/api.d.ts +510 -299
  5. package/dist/{archive-4ZQYK5MN.js → archive-UA4BDFXQ.js} +2 -2
  6. package/dist/bridge-FQHZL3MC.js +206 -0
  7. package/dist/chat-M4SX42JD.js +68 -0
  8. package/dist/{chunk-PHU4DEAJ.js → chunk-2WPW7OT6.js} +3 -3
  9. package/dist/{chunk-5Y3PBKW6.js → chunk-2YP2TVDT.js} +138 -56
  10. package/dist/{chunk-3CFRE2VC.js → chunk-AAPXKR5V.js} +435 -1090
  11. package/dist/{chunk-YJA7P64S.js → chunk-AW7PFDVN.js} +5 -5
  12. package/dist/{chunk-OZFKBXD6.js → chunk-EHYDTZTF.js} +6 -6
  13. package/dist/{chunk-WBHMQ5OZ.js → chunk-H7OZRFJB.js} +192 -12
  14. package/dist/{chunk-ON3FF5JA.js → chunk-HDN7MNGD.js} +3 -3
  15. package/dist/chunk-IAYBDWVG.js +477 -0
  16. package/dist/{chunk-TZKJLDQN.js → chunk-IKRVFPWU.js} +14 -9
  17. package/dist/{chunk-UTL75LP6.js → chunk-J4IBNXGJ.js} +20 -22
  18. package/dist/{chunk-WGOGUMPO.js → chunk-JGFVMROS.js} +13 -6
  19. package/dist/{chunk-NWI2425I.js → chunk-K5NAC55T.js} +1 -1
  20. package/dist/chunk-KTLFDYPT.js +61 -0
  21. package/dist/{chunk-V63B7DX3.js → chunk-LAC664WU.js} +7 -4
  22. package/dist/chunk-MD4C26II.js +128 -0
  23. package/dist/{chunk-USNBKHYG.js → chunk-NI5FFCCS.js} +12 -7
  24. package/dist/{chunk-3TV4GLFO.js → chunk-P72MVS4R.js} +4 -43
  25. package/dist/{chunk-2VO7453N.js → chunk-POSXWWTA.js} +30 -54
  26. package/dist/{chunk-XOXLRRR2.js → chunk-RKQEHRBB.js} +4 -3
  27. package/dist/{chunk-LX22GRG7.js → chunk-SGVNFZHW.js} +11 -8
  28. package/dist/chunk-T6HKBWXZ.js +23 -0
  29. package/dist/{chunk-J2CO4WEV.js → chunk-VIVMW2H2.js} +4 -4
  30. package/dist/{chunk-KTJGZ7M7.js → chunk-XBLSAVJF.js} +1 -1
  31. package/dist/cli.js +32 -49
  32. package/dist/{cloud-sync-NI2K3C7G.js → cloud-sync-HDL6PHZI.js} +14 -14
  33. package/dist/connectors/discord-bridge.js +158 -0
  34. package/dist/connectors/slack-bridge.js +119 -0
  35. package/dist/connectors/telegram-bridge.js +133 -0
  36. package/dist/conversations-M2K4253F.js +55 -0
  37. package/dist/create-D7J73A6H.js +45 -0
  38. package/dist/{create-4YBRTTJS.js → create-QWV73WXD.js} +1 -1
  39. package/dist/{daemon-client-Z7FAJ6JW.js → daemon-client-I42FK2BF.js} +2 -2
  40. package/dist/{daemon-restart-BJZ3O4U4.js → daemon-restart-G4B2OYAB.js} +7 -7
  41. package/dist/daemon.js +1889 -1216
  42. package/dist/db-IC4J52XQ.js +8 -0
  43. package/dist/{delete-27OYNK25.js → delete-4JYGD4VN.js} +1 -1
  44. package/dist/down-LVBXEULC.js +14 -0
  45. package/dist/{env-M336ONDP.js → env-YJMUMFIY.js} +2 -2
  46. package/dist/{export-HP4G5DQC.js → export-BOJQWBMA.js} +4 -4
  47. package/dist/files-M546TKVN.js +46 -0
  48. package/dist/{history-B64GTFTD.js → history-ALPTNB3I.js} +5 -5
  49. package/dist/{import-XIB7UV4S.js → import-SRTQXBGH.js} +4 -4
  50. package/dist/join-J4QU42DL.js +66 -0
  51. package/dist/list-R73GENNL.js +40 -0
  52. package/dist/{login-B5E7N7MY.js → login-3QZNR2DF.js} +4 -4
  53. package/dist/{login-6U7U6BNG.js → login-BKP3AFWN.js} +8 -18
  54. package/dist/logout-IQK7FNEK.js +20 -0
  55. package/dist/{logout-XSJRYS3U.js → logout-T53VKCPU.js} +4 -4
  56. package/dist/message-delivery-HV3S6HZV.js +24 -0
  57. package/dist/migrate-registry-to-db-XC7T5B7P.js +110 -0
  58. package/dist/{mind-HZ3QSDDJ.js → mind-S5V6CK5W.js} +29 -34
  59. package/dist/{mind-activity-tracker-4G6FURY2.js → mind-activity-tracker-EN6XNXPF.js} +3 -4
  60. package/dist/mind-list-UPJ75GPI.js +29 -0
  61. package/dist/mind-manager-S6ILZVX3.js +18 -0
  62. package/dist/{mind-sleep-DTV7L44D.js → mind-sleep-BTSWQNAC.js} +4 -4
  63. package/dist/mind-status-TK5AETEM.js +55 -0
  64. package/dist/{mind-wake-PFN4FN3T.js → mind-wake-SBAKIDVP.js} +4 -4
  65. package/dist/{notes-37FW2UR2.js → notes-XCER3I7M.js} +11 -21
  66. package/dist/{package-VZWLXPHV.js → package-CG4RWUGP.js} +1 -1
  67. package/dist/{pages-DIIT5HMQ.js → pages-KJDJX4TA.js} +5 -5
  68. package/dist/{publish-HQV7YREB.js → publish-ZZB33WP4.js} +9 -20
  69. package/dist/read-36UFXN3G.js +46 -0
  70. package/dist/{register-EFND67FQ.js → register-CHREOMJ3.js} +6 -25
  71. package/dist/{registry-D2BSQ2X5.js → registry-NDNOOYG4.js} +15 -9
  72. package/dist/reject-LXIZFJ4Q.js +39 -0
  73. package/dist/{restart-CCK7D6TV.js → restart-6ESL3NBO.js} +5 -5
  74. package/dist/{sandbox-EHGFF52K.js → sandbox-5BW5HPXM.js} +3 -3
  75. package/dist/{schedule-6F7ELB2M.js → schedule-QTJMFATP.js} +5 -5
  76. package/dist/{seed-E5OQGWX3.js → seed-SSUCYYDF.js} +2 -2
  77. package/dist/{send-IH6XZKPC.js → send-TAOEZ4NH.js} +87 -23
  78. package/dist/{setup-YGAAIKKZ.js → setup-JHL5ZEST.js} +2 -2
  79. package/dist/{setup-F6TWFYGQ.js → setup-RXYVGGT7.js} +9 -9
  80. package/dist/{skill-42LGFBQC.js → skill-AUAQTSP5.js} +5 -5
  81. package/dist/skills/dreaming/references/INSTALL.md +2 -2
  82. package/dist/skills/orientation/SKILL.md +3 -3
  83. package/dist/skills/shared-files/SKILL.md +44 -0
  84. package/dist/skills/shared-files/scripts/merge.ts +72 -0
  85. package/dist/skills/shared-files/scripts/pull.ts +52 -0
  86. package/dist/skills/volute-mind/SKILL.md +35 -34
  87. package/dist/sleep-manager-WMVG2VCL.js +28 -0
  88. package/dist/split-TKJ5OT3P.js +63 -0
  89. package/dist/{sprout-QL74KR2X.js → sprout-UNT7LKKE.js} +6 -7
  90. package/dist/{start-O5JQASRC.js → start-EUJSS5R4.js} +2 -2
  91. package/dist/status-NQJYR4BG.js +114 -0
  92. package/dist/{status-LV34BG6G.js → status-S7UUPNRW.js} +4 -14
  93. package/dist/{stop-2SOG5NYF.js → stop-3XAITBBF.js} +5 -5
  94. package/dist/systems-SMEFSHTA.js +60 -0
  95. package/dist/{tailscale-AJ4VL5XK.js → tailscale-NY5MUMY3.js} +1 -1
  96. package/dist/up-GM2JOH2Y.js +17 -0
  97. package/dist/{update-5VUDAI3D.js → update-PTSH22AZ.js} +9 -9
  98. package/dist/{update-check-F5Z3ALXX.js → update-check-64FWC4Y2.js} +2 -2
  99. package/dist/{upgrade-QCCO33BK.js → upgrade-HA47CS4C.js} +12 -5
  100. package/dist/variant-7TGZHOU3.js +41 -0
  101. package/dist/{version-notify-USFZBWMG.js → version-notify-JDUF4HQJ.js} +24 -29
  102. package/dist/web-assets/assets/index-BZGvToHi.css +1 -0
  103. package/dist/web-assets/assets/index-Cz4TrpzB.js +75 -0
  104. package/dist/web-assets/favicon.png +0 -0
  105. package/dist/web-assets/index.html +2 -2
  106. package/drizzle/0017_minds.sql +16 -0
  107. package/drizzle/meta/_journal.json +7 -0
  108. package/package.json +1 -1
  109. package/templates/_base/.init/.config/prompts.json +2 -2
  110. package/templates/_base/home/VOLUTE.md +5 -5
  111. package/templates/_base/src/lib/logger.ts +0 -4
  112. package/templates/_base/src/lib/startup.ts +2 -2
  113. package/dist/auth-4TV573WE.js +0 -37
  114. package/dist/channel-ZVZV42UD.js +0 -260
  115. package/dist/chunk-B2CPS4QU.js +0 -283
  116. package/dist/chunk-HFCBO2GL.js +0 -50
  117. package/dist/chunk-RWKVSSLY.js +0 -26
  118. package/dist/chunk-SIAG3QMM.js +0 -42
  119. package/dist/chunk-WSLPZF72.js +0 -173
  120. package/dist/connector-G722WXAU.js +0 -147
  121. package/dist/connectors/discord.js +0 -177
  122. package/dist/connectors/slack.js +0 -181
  123. package/dist/connectors/telegram.js +0 -187
  124. package/dist/down-7UKFMJJZ.js +0 -14
  125. package/dist/file-HUDKTRAS.js +0 -204
  126. package/dist/log-PBFNILJ4.js +0 -39
  127. package/dist/logout-UKD5LA37.js +0 -18
  128. package/dist/logs-3CART7O7.js +0 -77
  129. package/dist/merge-VK2HSKMA.js +0 -46
  130. package/dist/message-delivery-MS5JYPZX.js +0 -25
  131. package/dist/mind-manager-VVK67AY3.js +0 -19
  132. package/dist/pull-2MB4SK3C.js +0 -39
  133. package/dist/service-LLBV3R7M.js +0 -122
  134. package/dist/shared-UMO4S7CC.js +0 -39
  135. package/dist/sleep-manager-EE4NRN2Q.js +0 -29
  136. package/dist/status-FZBEBM7Q.js +0 -70
  137. package/dist/status-WXD4HXRL.js +0 -35
  138. package/dist/up-SDMCSVI3.js +0 -17
  139. package/dist/variant-WWLDY6D5.js +0 -207
  140. package/dist/web-assets/assets/index-CUQ31ieL.js +0 -69
  141. package/dist/web-assets/assets/index-CW8NSl1o.css +0 -1
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/connectors/bridge-sdk.ts
4
+ function loadBridgeEnv() {
5
+ const daemonUrl = process.env.VOLUTE_DAEMON_URL;
6
+ const daemonToken = process.env.VOLUTE_DAEMON_TOKEN;
7
+ const platform = process.env.VOLUTE_BRIDGE_PLATFORM;
8
+ if (!daemonUrl || !daemonToken || !platform) {
9
+ console.error(
10
+ "Missing required env vars: VOLUTE_DAEMON_URL, VOLUTE_DAEMON_TOKEN, VOLUTE_BRIDGE_PLATFORM"
11
+ );
12
+ process.exit(1);
13
+ }
14
+ return { daemonUrl, daemonToken, platform };
15
+ }
16
+ function getHeaders(env) {
17
+ return {
18
+ "Content-Type": "application/json",
19
+ Authorization: `Bearer ${env.daemonToken}`,
20
+ Origin: env.daemonUrl
21
+ };
22
+ }
23
+ async function sendToBridge(env, message) {
24
+ const url = `${env.daemonUrl}/api/bridges/${env.platform}/inbound`;
25
+ try {
26
+ const res = await fetch(url, {
27
+ method: "POST",
28
+ headers: getHeaders(env),
29
+ body: JSON.stringify(message)
30
+ });
31
+ if (!res.ok) {
32
+ const body = await res.text().catch(() => "");
33
+ console.error(`Bridge inbound returned ${res.status}: ${body}`);
34
+ return { ok: false, error: `Bridge returned ${res.status}` };
35
+ }
36
+ return await res.json();
37
+ } catch (err) {
38
+ const detail = err instanceof Error ? err.message : String(err);
39
+ console.error(`Failed to send bridge message: ${detail}`);
40
+ return { ok: false, error: `Failed to reach daemon: ${detail}` };
41
+ }
42
+ }
43
+ function onShutdown(cleanup) {
44
+ const handler = () => {
45
+ Promise.resolve(cleanup()).then(
46
+ () => process.exit(0),
47
+ (err) => {
48
+ console.error(`Shutdown error: ${err}`);
49
+ process.exit(1);
50
+ }
51
+ );
52
+ };
53
+ process.on("SIGINT", handler);
54
+ process.on("SIGTERM", handler);
55
+ }
56
+
57
+ export {
58
+ loadBridgeEnv,
59
+ sendToBridge,
60
+ onShutdown
61
+ };
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  execInherit
4
- } from "./chunk-YJA7P64S.js";
4
+ } from "./chunk-AW7PFDVN.js";
5
5
  import {
6
- voluteHome
7
- } from "./chunk-B2CPS4QU.js";
6
+ voluteHome,
7
+ voluteSystemDir
8
+ } from "./chunk-H7OZRFJB.js";
8
9
 
9
10
  // src/lib/service-mode.ts
10
11
  import { execFileSync } from "child_process";
@@ -151,7 +152,9 @@ async function restartService(mode) {
151
152
  }
152
153
  }
153
154
  function readDaemonConfig() {
154
- const configPath = resolve(voluteHome(), "daemon.json");
155
+ const newPath = resolve(voluteSystemDir(), "daemon.json");
156
+ const legacyPath = resolve(voluteHome(), "daemon.json");
157
+ const configPath = existsSync(newPath) ? newPath : legacyPath;
155
158
  if (!existsSync(configPath)) return { hostname: "127.0.0.1", port: 1618 };
156
159
  try {
157
160
  const config = JSON.parse(readFileSync(configPath, "utf-8"));
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ stateDir
4
+ } from "./chunk-H7OZRFJB.js";
5
+
6
+ // src/lib/file-sharing.ts
7
+ import { randomBytes } from "crypto";
8
+ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from "fs";
9
+ import { basename, join, normalize, resolve } from "path";
10
+ function validateFilePath(filePath) {
11
+ if (!filePath) return "File path is required";
12
+ const normalized = normalize(filePath);
13
+ if (normalized.startsWith("/") || normalized.startsWith("\\")) {
14
+ return "Absolute paths are not allowed";
15
+ }
16
+ if (normalized.includes("..")) {
17
+ return "Path traversal (..) is not allowed";
18
+ }
19
+ return null;
20
+ }
21
+ function pendingDir(receiver) {
22
+ return resolve(stateDir(receiver), "pending-files");
23
+ }
24
+ function validateId(id) {
25
+ if (!id || id.includes("/") || id.includes("\\") || id.includes("..")) {
26
+ throw new Error("Invalid pending file id");
27
+ }
28
+ }
29
+ function generateId(sender) {
30
+ const ts = Date.now();
31
+ const rand = randomBytes(2).toString("hex");
32
+ return `${sender}-${ts}-${rand}`;
33
+ }
34
+ function stageFile(receiver, sender, filename, content, originalPath) {
35
+ const err = validateFilePath(filename);
36
+ if (err) throw new Error(err);
37
+ if (sender.includes("/") || sender.includes("\\")) {
38
+ throw new Error("Invalid sender name");
39
+ }
40
+ const id = generateId(sender);
41
+ const dir = resolve(pendingDir(receiver), id);
42
+ mkdirSync(dir, { recursive: true });
43
+ const metadata = {
44
+ id,
45
+ sender,
46
+ filename: basename(filename),
47
+ originalPath,
48
+ size: content.length,
49
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
50
+ };
51
+ writeFileSync(resolve(dir, "metadata.json"), `${JSON.stringify(metadata, null, 2)}
52
+ `);
53
+ writeFileSync(resolve(dir, "data"), content);
54
+ return { id };
55
+ }
56
+ function listPending(receiver) {
57
+ const dir = pendingDir(receiver);
58
+ if (!existsSync(dir)) return [];
59
+ const entries = readdirSync(dir, { withFileTypes: true });
60
+ const result = [];
61
+ for (const entry of entries) {
62
+ if (!entry.isDirectory()) continue;
63
+ const metaPath = resolve(dir, entry.name, "metadata.json");
64
+ if (!existsSync(metaPath)) continue;
65
+ try {
66
+ result.push(JSON.parse(readFileSync(metaPath, "utf-8")));
67
+ } catch (err) {
68
+ console.warn(`[file-sharing] skipping malformed pending entry ${entry.name}:`, err);
69
+ }
70
+ }
71
+ return result.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
72
+ }
73
+ function getPending(receiver, id) {
74
+ validateId(id);
75
+ const metaPath = resolve(pendingDir(receiver), id, "metadata.json");
76
+ if (!existsSync(metaPath)) return null;
77
+ try {
78
+ return JSON.parse(readFileSync(metaPath, "utf-8"));
79
+ } catch (err) {
80
+ console.warn(`[file-sharing] failed to read pending metadata for ${id}:`, err);
81
+ return null;
82
+ }
83
+ }
84
+ function deliverFile(receiverDir, sender, filename, content, inboxPath) {
85
+ const err = validateFilePath(filename);
86
+ if (err) throw new Error(err);
87
+ const inbox = inboxPath ?? "inbox";
88
+ const inboxErr = validateFilePath(inbox);
89
+ if (inboxErr) throw new Error(`Invalid inboxPath: ${inboxErr}`);
90
+ if (sender.includes("/") || sender.includes("\\")) {
91
+ throw new Error("Invalid sender name");
92
+ }
93
+ const destDir = resolve(receiverDir, "home", inbox, sender);
94
+ mkdirSync(destDir, { recursive: true });
95
+ const destPath = resolve(destDir, basename(filename));
96
+ writeFileSync(destPath, content);
97
+ return join(inbox, sender, basename(filename));
98
+ }
99
+ function acceptPending(receiver, id, receiverDir, dest) {
100
+ const meta = getPending(receiver, id);
101
+ if (!meta) throw new Error(`Pending file not found: ${id}`);
102
+ const dataPath = resolve(pendingDir(receiver), id, "data");
103
+ const content = readFileSync(dataPath);
104
+ const inboxPath = dest ?? "inbox";
105
+ const destPath = deliverFile(receiverDir, meta.sender, meta.filename, content, inboxPath);
106
+ rmSync(resolve(pendingDir(receiver), id), { recursive: true });
107
+ return { sender: meta.sender, filename: meta.filename, destPath };
108
+ }
109
+ function rejectPending(receiver, id) {
110
+ const meta = getPending(receiver, id);
111
+ if (!meta) throw new Error(`Pending file not found: ${id}`);
112
+ rmSync(resolve(pendingDir(receiver), id), { recursive: true });
113
+ return { sender: meta.sender, filename: meta.filename };
114
+ }
115
+ function formatFileSize(bytes) {
116
+ if (bytes < 1024) return `${bytes} B`;
117
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
118
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
119
+ }
120
+
121
+ export {
122
+ validateFilePath,
123
+ stageFile,
124
+ listPending,
125
+ acceptPending,
126
+ rejectPending,
127
+ formatFileSize
128
+ };
@@ -1,18 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- getDb,
4
- sharedSkills
5
- } from "./chunk-WBHMQ5OZ.js";
6
2
  import {
7
3
  logger_default
8
4
  } from "./chunk-YUIHSKR6.js";
9
5
  import {
10
6
  exec,
11
7
  gitExec
12
- } from "./chunk-YJA7P64S.js";
8
+ } from "./chunk-AW7PFDVN.js";
13
9
  import {
10
+ getDb,
11
+ sharedSkills,
14
12
  voluteHome
15
- } from "./chunk-B2CPS4QU.js";
13
+ } from "./chunk-H7OZRFJB.js";
16
14
 
17
15
  // src/lib/skills.ts
18
16
  import { createHash } from "crypto";
@@ -30,7 +28,14 @@ import { basename, dirname, join, resolve } from "path";
30
28
  import { eq, sql } from "drizzle-orm";
31
29
  var VALID_SKILL_ID = /^[a-zA-Z0-9_-]+$/;
32
30
  var SEED_SKILLS = ["orientation", "memory"];
33
- var STANDARD_SKILLS = ["volute-mind", "memory", "sessions", "notes", "dreaming"];
31
+ var STANDARD_SKILLS = [
32
+ "volute-mind",
33
+ "memory",
34
+ "sessions",
35
+ "notes",
36
+ "dreaming",
37
+ "shared-files"
38
+ ];
34
39
  function validateSkillId(id) {
35
40
  if (!id || !VALID_SKILL_ID.test(id)) {
36
41
  throw new Error(`Invalid skill ID: ${id}`);
@@ -4,14 +4,14 @@ import {
4
4
  } from "./chunk-YUIHSKR6.js";
5
5
  import {
6
6
  gitExec
7
- } from "./chunk-YJA7P64S.js";
7
+ } from "./chunk-AW7PFDVN.js";
8
8
  import {
9
9
  isIsolationEnabled,
10
10
  mindUserName
11
- } from "./chunk-XOXLRRR2.js";
11
+ } from "./chunk-RKQEHRBB.js";
12
12
  import {
13
13
  voluteHome
14
- } from "./chunk-B2CPS4QU.js";
14
+ } from "./chunk-H7OZRFJB.js";
15
15
 
16
16
  // src/lib/shared.ts
17
17
  import { execFileSync } from "child_process";
@@ -178,50 +178,11 @@ async function sharedMerge(mindName, mindDir, message) {
178
178
  return { ok: true };
179
179
  });
180
180
  }
181
- async function sharedPull(mindName, mindDir) {
182
- return withSharedLock(async () => {
183
- const worktreePath = resolve(mindDir, "home", "shared");
184
- const status = (await gitExec(["status", "--porcelain"], { cwd: worktreePath })).trim();
185
- if (status) {
186
- await gitExec(["add", "-A"], { cwd: worktreePath });
187
- await gitExec(
188
- ["commit", "--author", `${mindName} <${mindName}@volute>`, "-m", `wip: ${mindName}`],
189
- { cwd: worktreePath }
190
- );
191
- }
192
- try {
193
- await gitExec(["rebase", "main"], { cwd: worktreePath });
194
- rechownWorktree(worktreePath, mindName);
195
- return { ok: true };
196
- } catch {
197
- try {
198
- await gitExec(["rebase", "--abort"], { cwd: worktreePath });
199
- } catch {
200
- return {
201
- ok: false,
202
- message: "Rebase failed and abort failed \u2014 shared worktree may need manual repair"
203
- };
204
- }
205
- return { ok: false, message: "Rebase failed \u2014 conflicts with main" };
206
- }
207
- });
208
- }
209
- async function sharedLog(limit = 20) {
210
- const dir = sharedDir();
211
- return gitExec(["log", "--oneline", "-n", String(limit), "main"], { cwd: dir });
212
- }
213
- async function sharedStatus(mindName) {
214
- const dir = sharedDir();
215
- return gitExec(["diff", `main...${mindName}`, "--stat"], { cwd: dir });
216
- }
217
181
 
218
182
  export {
219
183
  sharedDir,
220
184
  ensureSharedRepo,
221
185
  addSharedWorktree,
222
186
  removeSharedWorktree,
223
- sharedMerge,
224
- sharedPull,
225
- sharedLog,
226
- sharedStatus
187
+ sharedMerge
227
188
  };
@@ -2,32 +2,28 @@
2
2
  import {
3
3
  isSandboxEnabled,
4
4
  wrapForSandbox
5
- } from "./chunk-UTL75LP6.js";
5
+ } from "./chunk-J4IBNXGJ.js";
6
6
  import {
7
- getDb,
8
- mindHistory,
9
- systemPrompts
10
- } from "./chunk-WBHMQ5OZ.js";
7
+ loadMergedEnv
8
+ } from "./chunk-2WPW7OT6.js";
11
9
  import {
12
10
  logger_default
13
11
  } from "./chunk-YUIHSKR6.js";
14
- import {
15
- loadMergedEnv
16
- } from "./chunk-PHU4DEAJ.js";
17
12
  import {
18
13
  chownMindDir,
19
14
  isIsolationEnabled,
20
15
  wrapForIsolation
21
- } from "./chunk-XOXLRRR2.js";
16
+ } from "./chunk-RKQEHRBB.js";
22
17
  import {
23
18
  findMind,
24
- findVariant,
19
+ getDb,
25
20
  mindDir,
21
+ mindHistory,
26
22
  setMindRunning,
27
- setVariantRunning,
28
23
  stateDir,
29
- voluteHome
30
- } from "./chunk-B2CPS4QU.js";
24
+ systemPrompts,
25
+ voluteSystemDir
26
+ } from "./chunk-H7OZRFJB.js";
31
27
 
32
28
  // src/lib/daemon/mind-manager.ts
33
29
  import { execFile, spawn } from "child_process";
@@ -144,7 +140,7 @@ Have a conversation with the human. Explore what kind of mind you want to be. Wh
144
140
  category: "mind"
145
141
  },
146
142
  reply_instructions: {
147
- content: 'To reply to this message, use: volute send ${channel} "your message"',
143
+ content: 'To reply to this message, use: volute chat send ${channel} "your message"',
148
144
  description: "First-message reply hint injected via hook",
149
145
  variables: ["channel"],
150
146
  category: "mind"
@@ -160,7 +156,7 @@ Further messages will be saved to \${filePath}
160
156
 
161
157
  To accept, add to .config/routes.json:
162
158
  Rule: { "channel": "\${channel}", "session": "\${suggestedSession}" }
163
- \${batchRecommendation}To respond, use: volute send \${channel} "your message"
159
+ \${batchRecommendation}To respond, use: volute chat send \${channel} "your message"
164
160
  To reject, delete \${filePath}`,
165
161
  description: "New channel notification template",
166
162
  variables: [
@@ -313,9 +309,6 @@ function revokeMindToken(mindName) {
313
309
  function resolveMindToken(token) {
314
310
  return tokenToMind.get(token) ?? null;
315
311
  }
316
- function getMindToken(mindName) {
317
- return mindToToken.get(mindName) ?? null;
318
- }
319
312
 
320
313
  // src/lib/daemon/restart-tracker.ts
321
314
  var DEFAULT_MAX_ATTEMPTS = 5;
@@ -374,25 +367,23 @@ var MindManager = class {
374
367
  shuttingDown = false;
375
368
  restartTracker = new RestartTracker();
376
369
  pendingContext = /* @__PURE__ */ new Map();
377
- resolveTarget(name) {
378
- const [baseName, variantName] = name.split("@", 2);
379
- const entry = findMind(baseName);
380
- if (!entry) throw new Error(`Unknown mind: ${baseName}`);
381
- if (variantName) {
382
- const variant = findVariant(baseName, variantName);
383
- if (!variant) throw new Error(`Unknown variant: ${variantName} (mind: ${baseName})`);
384
- return { dir: variant.path, port: variant.port, isVariant: true, baseName, variantName };
370
+ async resolveTarget(name) {
371
+ const entry = await findMind(name);
372
+ if (!entry) throw new Error(`Unknown mind: ${name}`);
373
+ if (entry.parent) {
374
+ if (!entry.dir) throw new Error(`Variant ${name} has no directory`);
375
+ return { dir: entry.dir, port: entry.port, baseName: entry.parent };
385
376
  }
386
- const dir = mindDir(baseName);
377
+ const dir = mindDir(name);
387
378
  if (!existsSync3(dir)) throw new Error(`Mind directory missing: ${dir}`);
388
- return { dir, port: entry.port, isVariant: false, baseName };
379
+ return { dir, port: entry.port, baseName: name };
389
380
  }
390
381
  async startMind(name) {
391
382
  if (this.minds.has(name)) {
392
383
  throw new Error(`Mind ${name} is already running`);
393
384
  }
394
- const target = this.resolveTarget(name);
395
- const { dir, isVariant, baseName, variantName } = target;
385
+ const target = await this.resolveTarget(name);
386
+ const { dir, baseName } = target;
396
387
  const port = target.port;
397
388
  const pidFile = mindPidPath(name);
398
389
  try {
@@ -463,7 +454,7 @@ var MindManager = class {
463
454
  let spawnCmd;
464
455
  let spawnArgs;
465
456
  if (isIsolationEnabled()) {
466
- [spawnCmd, spawnArgs] = wrapForIsolation(tsxBin, tsxArgs, name);
457
+ [spawnCmd, spawnArgs] = await wrapForIsolation(tsxBin, tsxArgs, name);
467
458
  } else if (isSandboxEnabled()) {
468
459
  [spawnCmd, spawnArgs] = await wrapForSandbox(tsxBin, tsxArgs, dir, name);
469
460
  } else {
@@ -519,11 +510,7 @@ var MindManager = class {
519
510
  }
520
511
  if (this.restartTracker.reset(name)) this.saveCrashAttempts();
521
512
  this.setupCrashRecovery(name, child);
522
- if (isVariant) {
523
- setVariantRunning(baseName, variantName, true);
524
- } else {
525
- setMindRunning(name, true);
526
- }
513
+ await setMindRunning(name, true);
527
514
  mlog.info(`started mind ${name} on port ${port}`);
528
515
  await this.deliverPendingContext(name);
529
516
  }
@@ -581,7 +568,7 @@ var MindManager = class {
581
568
  if (this.shuttingDown || this.stopping.has(name)) return;
582
569
  mlog.error(`mind ${name} exited with code ${code}`);
583
570
  try {
584
- const { getSleepManagerIfReady } = await import("./sleep-manager-EE4NRN2Q.js");
571
+ const { getSleepManagerIfReady } = await import("./sleep-manager-WMVG2VCL.js");
585
572
  const sleepState = getSleepManagerIfReady()?.getState(name);
586
573
  if (sleepState?.sleeping) {
587
574
  mlog.info(`${name} is sleeping \u2014 skipping crash recovery`);
@@ -590,20 +577,15 @@ var MindManager = class {
590
577
  } catch (err) {
591
578
  mlog.warn(`failed to check sleep state for ${name}`, logger_default.errorData(err));
592
579
  }
593
- import("./mind-activity-tracker-4G6FURY2.js").then(({ markIdle }) => markIdle(name)).catch((err) => mlog.warn(`failed to mark ${name} idle after crash`, logger_default.errorData(err)));
594
- import("./activity-events-ZMBAKLUF.js").then(
580
+ import("./mind-activity-tracker-EN6XNXPF.js").then(({ markIdle }) => markIdle(name)).catch((err) => mlog.warn(`failed to mark ${name} idle after crash`, logger_default.errorData(err)));
581
+ import("./activity-events-BBIEA2F4.js").then(
595
582
  ({ publish }) => publish({ type: "mind_stopped", mind: name, summary: `${name} crashed (exit ${code})` })
596
583
  ).catch((err) => mlog.warn(`failed to publish crash event for ${name}`, logger_default.errorData(err)));
597
584
  const { shouldRestart, delay, attempt } = this.restartTracker.recordCrash(name);
598
585
  this.saveCrashAttempts();
599
586
  if (!shouldRestart) {
600
587
  mlog.error(`${name} crashed ${attempt} times \u2014 giving up on restart`);
601
- const [base, variant] = name.split("@", 2);
602
- if (variant) {
603
- setVariantRunning(base, variant, false);
604
- } else {
605
- setMindRunning(name, false);
606
- }
588
+ await setMindRunning(name, false);
607
589
  return;
608
590
  }
609
591
  mlog.info(
@@ -643,12 +625,7 @@ var MindManager = class {
643
625
  if (this.restartTracker.reset(name)) this.saveCrashAttempts();
644
626
  rmSync2(mindPidPath(name), { force: true });
645
627
  if (!this.shuttingDown) {
646
- const [baseName, variantName] = name.split("@", 2);
647
- if (variantName) {
648
- setVariantRunning(baseName, variantName, false);
649
- } else {
650
- setMindRunning(name, false);
651
- }
628
+ await setMindRunning(name, false);
652
629
  }
653
630
  mlog.info(`stopped mind ${name}`);
654
631
  }
@@ -668,7 +645,7 @@ var MindManager = class {
668
645
  return [...this.minds.keys()];
669
646
  }
670
647
  get crashAttemptsPath() {
671
- return resolve(voluteHome(), "crash-attempts.json");
648
+ return resolve(voluteSystemDir(), "crash-attempts.json");
672
649
  }
673
650
  loadCrashAttempts() {
674
651
  this.restartTracker.load(loadJsonMap(this.crashAttemptsPath));
@@ -721,8 +698,6 @@ function getMindManager() {
721
698
 
722
699
  export {
723
700
  RotatingLog,
724
- resolveMindToken,
725
- getMindToken,
726
701
  RestartTracker,
727
702
  PROMPT_KEYS,
728
703
  PROMPT_DEFAULTS,
@@ -733,6 +708,7 @@ export {
733
708
  loadJsonMap,
734
709
  saveJsonMap,
735
710
  clearJsonMap,
711
+ resolveMindToken,
736
712
  MindManager,
737
713
  initMindManager,
738
714
  getMindManager
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ getBaseName,
3
4
  validateMindName
4
- } from "./chunk-B2CPS4QU.js";
5
+ } from "./chunk-H7OZRFJB.js";
5
6
 
6
7
  // src/lib/isolation.ts
7
8
  import { execFileSync } from "child_process";
@@ -138,9 +139,9 @@ function deleteMindUser(name) {
138
139
  } catch {
139
140
  }
140
141
  }
141
- function wrapForIsolation(cmd, args, mindName) {
142
+ async function wrapForIsolation(cmd, args, mindName) {
142
143
  if (!isIsolationEnabled()) return [cmd, args];
143
- const baseName = mindName.split("@", 2)[0];
144
+ const baseName = await getBaseName(mindName);
144
145
  const user = mindUserName(baseName);
145
146
  if (process.platform === "darwin") {
146
147
  return ["sudo", ["-u", user, "--", cmd, ...args]];
@@ -4,16 +4,17 @@ import {
4
4
  modeLabel,
5
5
  pollHealth,
6
6
  startService
7
- } from "./chunk-V63B7DX3.js";
7
+ } from "./chunk-LAC664WU.js";
8
+ import {
9
+ readGlobalConfig
10
+ } from "./chunk-IKRVFPWU.js";
8
11
  import {
9
12
  parseArgs
10
13
  } from "./chunk-D424ZQGI.js";
11
14
  import {
12
- readGlobalConfig
13
- } from "./chunk-TZKJLDQN.js";
14
- import {
15
- voluteHome
16
- } from "./chunk-B2CPS4QU.js";
15
+ voluteHome,
16
+ voluteSystemDir
17
+ } from "./chunk-H7OZRFJB.js";
17
18
 
18
19
  // src/commands/up.ts
19
20
  import { spawn } from "child_process";
@@ -51,7 +52,8 @@ async function run(args) {
51
52
  const port = flags.port ?? config.port ?? 1618;
52
53
  const hostname = flags.host ?? config.hostname ?? "127.0.0.1";
53
54
  const home = voluteHome();
54
- const pidPath = resolve(home, "daemon.pid");
55
+ const systemDir = voluteSystemDir();
56
+ const pidPath = resolve(systemDir, "daemon.pid");
55
57
  if (existsSync(pidPath)) {
56
58
  try {
57
59
  const pid = parseInt(readFileSync(pidPath, "utf-8").trim(), 10);
@@ -103,7 +105,8 @@ async function run(args) {
103
105
  process.exit(1);
104
106
  }
105
107
  mkdirSync(home, { recursive: true });
106
- const logFile = resolve(home, "daemon.log");
108
+ mkdirSync(systemDir, { recursive: true });
109
+ const logFile = resolve(systemDir, "daemon.log");
107
110
  const logFd = openSync(logFile, "a");
108
111
  const daemonArgs = [daemonModule, "--port", String(port), "--host", hostname];
109
112
  if (flags.tailscale) daemonArgs.push("--tailscale");
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/slugify.ts
4
+ function slugify(text) {
5
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
6
+ }
7
+ function buildVoluteSlug(opts) {
8
+ if (opts.convType === "channel" && opts.convName) {
9
+ return `volute:#${opts.convName}`;
10
+ }
11
+ const isDM = opts.participants.length === 2;
12
+ if (isDM) {
13
+ const other = opts.participants.find((p) => p.username !== opts.mindUsername);
14
+ const otherSlug = other ? slugify(other.username) : "";
15
+ return otherSlug ? `volute:@${otherSlug}` : `volute:${opts.conversationId}`;
16
+ }
17
+ return opts.convTitle ? `volute:${slugify(opts.convTitle)}` : `volute:${opts.conversationId}`;
18
+ }
19
+
20
+ export {
21
+ slugify,
22
+ buildVoluteSlug
23
+ };
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- activity,
4
- getDb
5
- } from "./chunk-WBHMQ5OZ.js";
6
2
  import {
7
3
  logger_default
8
4
  } from "./chunk-YUIHSKR6.js";
5
+ import {
6
+ activity,
7
+ getDb
8
+ } from "./chunk-H7OZRFJB.js";
9
9
 
10
10
  // src/lib/events/activity-events.ts
11
11
  var subscribers = /* @__PURE__ */ new Set();
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  mindDir,
4
4
  stateDir
5
- } from "./chunk-B2CPS4QU.js";
5
+ } from "./chunk-H7OZRFJB.js";
6
6
 
7
7
  // src/lib/archive.ts
8
8
  import { execFileSync } from "child_process";