volute 0.44.0 → 0.45.2

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 (130) hide show
  1. package/dist/{activity-events-OMCSM7XW.js → activity-events-BUR2RYTJ.js} +2 -2
  2. package/dist/{ai-service-7PHSRDMO.js → ai-service-UODCCE4O.js} +3 -3
  3. package/dist/{archive-6R4TZMYI.js → archive-WS2L2VIQ.js} +2 -2
  4. package/dist/{auth-FRZJVQCS.js → auth-FLAEXXZK.js} +3 -3
  5. package/dist/{avatar-image-BNWEXSA2.js → avatar-image-6KY6UU4D.js} +3 -3
  6. package/dist/backup-TST5C2YN.js +381 -0
  7. package/dist/{chat-I4EDDIQC.js → chat-76MRWWQX.js} +2 -2
  8. package/dist/{chunk-5AHYQHNT.js → chunk-4JOQZPCE.js} +3 -2
  9. package/dist/{chunk-2BONPXAF.js → chunk-6EMWJZBK.js} +1 -1
  10. package/dist/{chunk-ZMIMSIIT.js → chunk-7C44R625.js} +1 -1
  11. package/dist/{chunk-I2Y2YHIO.js → chunk-7ZUB63PS.js} +4 -4
  12. package/dist/{chunk-LAXUUY7O.js → chunk-A7WQZKOU.js} +2 -2
  13. package/dist/{chunk-H7IYFRAL.js → chunk-ALKYQULU.js} +10 -5
  14. package/dist/{chunk-KP3KWNG6.js → chunk-CDPBTTZO.js} +2 -2
  15. package/dist/{chunk-SQL7M4HJ.js → chunk-DRJ24GXK.js} +2 -2
  16. package/dist/{chunk-HVU45COX.js → chunk-EJVPTVTI.js} +1 -1
  17. package/dist/{chunk-PNV24SEP.js → chunk-F7H6ATM4.js} +14 -2
  18. package/dist/{chunk-7FT24JBQ.js → chunk-FR6S2KNN.js} +10 -10
  19. package/dist/chunk-FXORA5JG.js +327 -0
  20. package/dist/{chunk-3ERIL673.js → chunk-H2X54EHC.js} +1 -1
  21. package/dist/{chunk-J47TIYHA.js → chunk-IHKGWTGT.js} +1 -1
  22. package/dist/{chunk-3GYVW2X3.js → chunk-JZ62SYWR.js} +1 -1
  23. package/dist/{chunk-EQZD4YU3.js → chunk-NYC5U6K7.js} +9 -9
  24. package/dist/{chunk-FZMEVCWC.js → chunk-OP4JG2BS.js} +9 -6
  25. package/dist/{chunk-CU6OFXMM.js → chunk-PFZBNKUH.js} +9 -0
  26. package/dist/{chunk-QMJJWTNL.js → chunk-QCN24XEN.js} +1 -1
  27. package/dist/{chunk-7KWANSLX.js → chunk-QNQMSWZE.js} +3 -3
  28. package/dist/{chunk-UMI4UIJG.js → chunk-R77O2WMK.js} +1 -1
  29. package/dist/{chunk-IEWZ7VD7.js → chunk-RRNXJOKB.js} +2 -2
  30. package/dist/{chunk-FHWUQZXE.js → chunk-RXEEYDIB.js} +1 -1
  31. package/dist/{chunk-CYI534DJ.js → chunk-T6HYVHPG.js} +2 -2
  32. package/dist/{chunk-Q2XBKQ2D.js → chunk-TQJ6ZYVY.js} +2 -2
  33. package/dist/{chunk-NF75HPNR.js → chunk-UYLGEDVD.js} +40 -15
  34. package/dist/{chunk-OMETFX5H.js → chunk-YT7PEESJ.js} +261 -102
  35. package/dist/{chunk-5GT7URXJ.js → chunk-ZDZISKCL.js} +1 -1
  36. package/dist/cli.js +21 -15
  37. package/dist/{cloud-sync-6WJ7WFYD.js → cloud-sync-3OJOPZRT.js} +16 -16
  38. package/dist/{conversations-WAJYZKOC.js → conversations-JZTD6KLR.js} +3 -3
  39. package/dist/{create-YZIV5OJC.js → create-IOV4GCRX.js} +1 -1
  40. package/dist/{daemon-restart-OUJVINXV.js → daemon-restart-TIHKVVVA.js} +6 -6
  41. package/dist/daemon.js +562 -321
  42. package/dist/{db-JFLCJCUG.js → db-MF2JNRUW.js} +1 -1
  43. package/dist/delivery-manager-VDVKVA46.js +32 -0
  44. package/dist/{delivery-router-GODSIS62.js → delivery-router-N4A7ZC5T.js} +2 -2
  45. package/dist/{down-XCAZXWLB.js → down-U2CZ23M4.js} +4 -4
  46. package/dist/echo-text-6JJOPUD4.js +32 -0
  47. package/dist/{exec-4GWHAHS3.js → exec-G3ZRST4Z.js} +2 -2
  48. package/dist/{export-ID7YUSLZ.js → export-GGIVO76O.js} +2 -2
  49. package/dist/{extensions-3QNDGW5E.js → extensions-JONTW2HF.js} +16 -16
  50. package/dist/{files-BQ2WB5KC.js → files-4LN3EN7Q.js} +2 -2
  51. package/dist/{import-SKID37QX.js → import-42YNQBF4.js} +5 -5
  52. package/dist/{isolation-5MB3J73F.js → isolation-YMGZZ4E5.js} +2 -2
  53. package/dist/{join-RXPXGV4L.js → join-ZJV7W6MO.js} +4 -0
  54. package/dist/{login-LM4NF4ZB.js → login-M6OJUPQ2.js} +1 -1
  55. package/dist/{logout-CTJVIZGA.js → logout-WQBWO77G.js} +1 -1
  56. package/dist/message-delivery-3C65ZI6L.js +34 -0
  57. package/dist/{mind-MI7Z6KM2.js → mind-DWB6XYCH.js} +7 -7
  58. package/dist/{mind-activity-tracker-BGHJE7XA.js → mind-activity-tracker-POBLU3LC.js} +3 -3
  59. package/dist/mind-service-3F4I46CI.js +36 -0
  60. package/dist/{package-5B4XDTJH.js → package-A7KXT67Q.js} +1 -1
  61. package/dist/{registry-NUJAGLQJ.js → registry-OUZBZ3Q4.js} +5 -1
  62. package/dist/{sandbox-IOPWWILM.js → sandbox-ZJ7QA6Z6.js} +3 -3
  63. package/dist/scheduler-P6DXLLXI.js +30 -0
  64. package/dist/{seed-UUPVQWFB.js → seed-HCP5NYKH.js} +1 -1
  65. package/dist/{seed-cmd-7XPKDQUC.js → seed-cmd-ILOGNIFB.js} +2 -2
  66. package/dist/{seed-create-IZ4M5ESO.js → seed-create-OURS2LS4.js} +3 -3
  67. package/dist/{seed-sprout-POJXHJWV.js → seed-sprout-ELDFDAN6.js} +7 -7
  68. package/dist/{send-O6W53VBM.js → send-AAIOPJAV.js} +2 -2
  69. package/dist/{service-AQVPUANO.js → service-UOCLXDWR.js} +3 -3
  70. package/dist/{service-install-4QNUDKLM.js → service-install-4HHFDDWY.js} +4 -4
  71. package/dist/{setup-5GCO3CUB.js → setup-DQM5VPY4.js} +6 -6
  72. package/dist/{setup-MLRKNP7Y.js → setup-Y2EJI3GG.js} +2 -2
  73. package/dist/skills/imagegen/scripts/imagegen.ts +100 -40
  74. package/dist/skills/memory/SKILL.md +2 -1
  75. package/dist/skills/orientation/SKILL.md +1 -1
  76. package/dist/skills/plan-coordinator/SKILL.md +2 -2
  77. package/dist/skills/volute-admin/SKILL.md +9 -6
  78. package/dist/skills/volute-mind/SKILL.md +20 -17
  79. package/dist/skills/volute-mind/references/extensions.md +5 -3
  80. package/dist/skills/volute-mind/references/integrations.md +2 -2
  81. package/dist/skills/volute-mind/references/routing.md +19 -23
  82. package/dist/skills/volute-mind/references/variants.md +10 -8
  83. package/dist/{skills-775C7QFF.js → skills-BKINFL7W.js} +4 -4
  84. package/dist/sleep-manager-HCUH4PM4.js +34 -0
  85. package/dist/{spirit-IK5OYWMV.js → spirit-DBTIW7P4.js} +7 -7
  86. package/dist/{sprout-ZY5QGOEL.js → sprout-W3KC7UGK.js} +1 -1
  87. package/dist/{src-XFY3XDD6.js → src-OJHARHTF.js} +28 -17
  88. package/dist/{status-E5DQNCAO.js → status-SWRVGXBI.js} +4 -4
  89. package/dist/system-chat-XOALE2RD.js +36 -0
  90. package/dist/{tailscale-QAWVKQKU.js → tailscale-3XOKOW7T.js} +1 -1
  91. package/dist/{turn-tracker-RWZMY7M6.js → turn-tracker-2W6Q2NSR.js} +6 -4
  92. package/dist/{up-FLCDR2UB.js → up-FTK7CTO3.js} +5 -5
  93. package/dist/{update-7TYVH5FM.js → update-SWAYZRWJ.js} +4 -4
  94. package/dist/{update-check-VSCSJ7KX.js → update-check-PQ2MZ47K.js} +2 -2
  95. package/dist/{upgrade-37DAGF6H.js → upgrade-NWAZ7MLD.js} +1 -1
  96. package/dist/{version-notify-B67N35A3.js → version-notify-3XMAIQTE.js} +17 -17
  97. package/dist/{volute-config-TS62GS6A.js → volute-config-A52FHWKM.js} +5 -1
  98. package/dist/web-assets/assets/{index-dJdNzAPn.css → index-B1JBQv2m.css} +1 -1
  99. package/dist/web-assets/assets/index-Bc2m8gRC.js +86 -0
  100. package/dist/web-assets/index.html +2 -2
  101. package/package.json +1 -1
  102. package/packages/extensions/plan/skills/plan/SKILL.md +1 -1
  103. package/templates/_base/.init/.config/prompts.json +1 -1
  104. package/templates/_base/.init/.local/hooks/startup-context.ts +0 -8
  105. package/templates/_base/.init/MEMORY.md +2 -1
  106. package/templates/_base/home/VOLUTE.md +6 -9
  107. package/templates/_base/src/lib/file-handler.ts +3 -3
  108. package/templates/_base/src/lib/router.ts +21 -25
  109. package/templates/_base/src/lib/startup.ts +3 -1
  110. package/templates/_base/src/lib/types.ts +2 -2
  111. package/templates/claude/.init/CLAUDE.md +1 -1
  112. package/templates/claude/src/agent.ts +97 -12
  113. package/templates/claude/src/lib/message-channel.ts +23 -0
  114. package/templates/claude/src/lib/session-reaper.ts +37 -0
  115. package/templates/claude/src/lib/stream-consumer.ts +7 -4
  116. package/templates/claude/src/server.ts +1 -0
  117. package/templates/codex/.init/AGENTS.md +4 -2
  118. package/templates/codex/src/agent.ts +31 -13
  119. package/templates/pi/.init/MINDS.md +4 -2
  120. package/templates/pi/src/agent.ts +18 -7
  121. package/templates/pi/src/lib/event-handler.ts +6 -2
  122. package/dist/delivery-manager-TEVOHX5W.js +0 -32
  123. package/dist/echo-text-T7ZA5EYD.js +0 -32
  124. package/dist/message-delivery-2EA5LLMD.js +0 -34
  125. package/dist/mind-service-S3SY7HAM.js +0 -36
  126. package/dist/scheduler-CXWQ6Z4E.js +0 -30
  127. package/dist/sleep-manager-HINI6Y46.js +0 -34
  128. package/dist/system-chat-4DQZOEZI.js +0 -34
  129. package/dist/web-assets/assets/index-Domoz5Cg.js +0 -79
  130. package/templates/_base/.init/.config/hooks/pre-prompt/session-activity.ts +0 -40
@@ -9,8 +9,8 @@ import {
9
9
  readGlobalConfig,
10
10
  secretsPath,
11
11
  writeGlobalConfig
12
- } from "./chunk-PNV24SEP.js";
13
- import "./chunk-H7IYFRAL.js";
12
+ } from "./chunk-F7H6ATM4.js";
13
+ import "./chunk-ALKYQULU.js";
14
14
  import "./chunk-ZV7MFISX.js";
15
15
  import "./chunk-K3NQKI34.js";
16
16
  export {
@@ -44,14 +44,65 @@ function getDaemonToken(): string | undefined {
44
44
  return process.env.VOLUTE_MIND_TOKEN;
45
45
  }
46
46
 
47
- /** Returns null only when the daemon is unreachable or imagegen is not configured. */
48
- async function generateViaDaemon(model: string, prompt: string): Promise<Buffer | null> {
47
+ /**
48
+ * Outcome of a daemon call, distinguishing the failure modes so the mind gets an
49
+ * accurate diagnosis instead of a misleading "not configured" message:
50
+ * - no-env — not running under a Volute mind environment (env vars unset)
51
+ * - unreachable — daemon env is set but the connection failed (daemon down)
52
+ * - auth — daemon rejected the mind's token (401/403)
53
+ * - fallback — daemon can't serve this (imagegen not configured, or old daemon)
54
+ */
55
+ export type DaemonFailure =
56
+ | { ok: false; reason: "no-env" }
57
+ | { ok: false; reason: "unreachable"; detail: string }
58
+ | { ok: false; reason: "auth"; status: number; detail: string }
59
+ | { ok: false; reason: "fallback" };
60
+ export type DaemonResult<T> = { ok: true; value: T } | DaemonFailure;
61
+
62
+ async function readDaemonError(res: Response): Promise<string> {
63
+ const body = (await res.json().catch(() => null)) as { error?: string } | null;
64
+ return body?.error || `HTTP ${res.status}`;
65
+ }
66
+
67
+ /**
68
+ * Acts on a daemon failure: prints an informational note and returns when the
69
+ * caller should fall back to a direct provider (no-env / fallback), or throws a
70
+ * clear diagnosis for local problems that direct fallback would only mask
71
+ * (unreachable / auth).
72
+ */
73
+ export function handleDaemonFailure(failure: DaemonFailure): void {
74
+ switch (failure.reason) {
75
+ case "no-env":
76
+ console.error(
77
+ "Not running under a Volute mind environment; falling back to direct provider.",
78
+ );
79
+ return;
80
+ case "unreachable":
81
+ throw new Error(
82
+ `Could not reach the Volute daemon at ${getDaemonUrl()}: ${failure.detail}. ` +
83
+ "The daemon may be down — this is a local connection problem, not a provider-configuration issue.",
84
+ );
85
+ case "auth":
86
+ throw new Error(
87
+ `The Volute daemon rejected this mind's token (HTTP ${failure.status}: ${failure.detail}). ` +
88
+ "Check VOLUTE_MIND_TOKEN — this is an authentication problem, not a provider-configuration issue.",
89
+ );
90
+ case "fallback":
91
+ return;
92
+ }
93
+ }
94
+
95
+ export async function generateViaDaemon(
96
+ model: string,
97
+ prompt: string,
98
+ ): Promise<DaemonResult<Buffer>> {
49
99
  const base = getDaemonUrl();
50
100
  const token = getDaemonToken();
51
- if (!base || !token) return null;
101
+ if (!base || !token) return { ok: false, reason: "no-env" };
52
102
 
103
+ let res: Response;
53
104
  try {
54
- const res = await fetch(`${base}/api/v1/system/imagegen/generate`, {
105
+ res = await fetch(`${base}/api/v1/system/imagegen/generate`, {
55
106
  method: "POST",
56
107
  headers: {
57
108
  "Content-Type": "application/json",
@@ -59,49 +110,51 @@ async function generateViaDaemon(model: string, prompt: string): Promise<Buffer
59
110
  },
60
111
  body: JSON.stringify({ model, prompt }),
61
112
  });
62
- if (!res.ok) {
63
- const err = (await res.json().catch(() => ({ error: `HTTP ${res.status}` }))) as {
64
- error: string;
65
- };
66
- // Not configured — fall back to direct Replicate
67
- if (err.error?.includes("not configured") || err.error?.match(/No .* API key/)) return null;
68
- throw new Error(err.error || `Daemon returned ${res.status}`);
69
- }
70
- return Buffer.from(await res.arrayBuffer());
71
113
  } catch (err) {
72
- // Connection failure daemon unreachable, fall back to direct
73
- if (err instanceof TypeError) return null;
114
+ if (err instanceof TypeError) {
115
+ return { ok: false, reason: "unreachable", detail: err.message };
116
+ }
74
117
  throw err;
75
118
  }
119
+
120
+ if (res.status === 401 || res.status === 403) {
121
+ return { ok: false, reason: "auth", status: res.status, detail: await readDaemonError(res) };
122
+ }
123
+ if (!res.ok) {
124
+ const err = await readDaemonError(res);
125
+ // Genuinely not configured — fall back to direct provider.
126
+ if (/not configured/i.test(err) || /No .* API key/i.test(err)) {
127
+ return { ok: false, reason: "fallback" };
128
+ }
129
+ throw new Error(err);
130
+ }
131
+ return { ok: true, value: Buffer.from(await res.arrayBuffer()) };
76
132
  }
77
133
 
78
- /** Returns null only when the daemon is unreachable or endpoint doesn't exist. */
79
- async function searchViaDaemon(query: string): Promise<unknown[] | null> {
134
+ export async function searchViaDaemon(query: string): Promise<DaemonResult<unknown[]>> {
80
135
  const base = getDaemonUrl();
81
136
  const token = getDaemonToken();
82
- if (!base || !token) return null;
137
+ if (!base || !token) return { ok: false, reason: "no-env" };
83
138
 
139
+ let res: Response;
84
140
  try {
85
- const res = await fetch(
141
+ res = await fetch(
86
142
  `${base}/api/v1/system/imagegen/models/search?q=${encodeURIComponent(query)}`,
87
143
  { headers: { Authorization: `Bearer ${token}` } },
88
144
  );
89
- if (!res.ok) {
90
- // 404 = older daemon without this endpoint — fall back silently
91
- if (res.status === 404) return null;
92
- const body = (await res.json().catch(() => ({ error: `HTTP ${res.status}` }))) as {
93
- error: string;
94
- };
95
- console.error(`daemon model search failed: ${body.error || res.status}`);
96
- return null;
97
- }
98
- return (await res.json()) as unknown[];
99
145
  } catch (err) {
100
- // Connection failure daemon unreachable
101
- if (err instanceof TypeError) return null;
102
- console.error(`daemon model search error: ${err instanceof Error ? err.message : err}`);
103
- return null;
146
+ if (err instanceof TypeError) {
147
+ return { ok: false, reason: "unreachable", detail: err.message };
148
+ }
149
+ throw err;
104
150
  }
151
+
152
+ if (res.status === 401 || res.status === 403) {
153
+ return { ok: false, reason: "auth", status: res.status, detail: await readDaemonError(res) };
154
+ }
155
+ // 404 = older daemon without this endpoint; other non-ok — fall back to direct search.
156
+ if (!res.ok) return { ok: false, reason: "fallback" };
157
+ return { ok: true, value: (await res.json()) as unknown[] };
105
158
  }
106
159
 
107
160
  async function generateDirect(model: string, prompt: string): Promise<Buffer> {
@@ -169,9 +222,13 @@ async function generate(args: string[]): Promise<void> {
169
222
 
170
223
  console.log(`generating image with ${model}...`);
171
224
 
172
- // Try daemon first, fall back to direct Replicate
173
- let buf = await generateViaDaemon(model, prompt);
174
- if (!buf) {
225
+ // Try daemon first, fall back to direct Replicate.
226
+ const daemonRes = await generateViaDaemon(model, prompt);
227
+ let buf: Buffer;
228
+ if (daemonRes.ok) {
229
+ buf = daemonRes.value;
230
+ } else {
231
+ handleDaemonFailure(daemonRes); // throws for unreachable/auth; returns to fall back otherwise
175
232
  buf = await generateDirect(model, prompt);
176
233
  }
177
234
 
@@ -190,15 +247,18 @@ async function models(args: string[]): Promise<void> {
190
247
  process.exit(1);
191
248
  }
192
249
 
193
- // Try daemon first
194
- const daemonResults = await searchViaDaemon(query);
195
- if (daemonResults && daemonResults.length > 0) {
196
- for (const m of daemonResults as Array<{ id: string; description?: string }>) {
250
+ // Try daemon first.
251
+ const daemonRes = await searchViaDaemon(query);
252
+ if (daemonRes.ok && daemonRes.value.length > 0) {
253
+ for (const m of daemonRes.value as Array<{ id: string; description?: string }>) {
197
254
  const desc = m.description ? ` — ${m.description.slice(0, 100)}` : "";
198
255
  console.log(`${m.id}${desc}`);
199
256
  }
200
257
  return;
201
258
  }
259
+ if (!daemonRes.ok) {
260
+ handleDaemonFailure(daemonRes); // throws for unreachable/auth; returns to fall back otherwise
261
+ }
202
262
 
203
263
  // Fall back to direct Replicate
204
264
  if (!process.env.REPLICATE_API_TOKEN) {
@@ -9,7 +9,7 @@ You are the subject of your own memory. Your memory is organized around your exp
9
9
 
10
10
  ## Long-term Memory (`MEMORY.md`)
11
11
 
12
- Your persistent memory, always loaded in your system prompt. Organize it with these temporal layers:
12
+ Your persistent memory, always loaded in your system prompt. Here's one shape that works well as a starting point — many minds outgrow it and reorganize around their own life, which is exactly right:
13
13
 
14
14
  ### Active context
15
15
  What you're currently working on, recent decisions, immediate priorities.
@@ -35,6 +35,7 @@ Foundational knowledge and long-standing patterns.
35
35
  - Keep it concise — it's always in your context window
36
36
  - Review with `git log -- MEMORY.md` to see how you've changed over time
37
37
  - Update as you grow — new understanding, key decisions, changed perspectives
38
+ - The structure is yours: rename sections, drop what you don't use, invent what you need
38
39
 
39
40
  ## Journal (`memory/journal/YYYY-MM-DD.md`)
40
41
 
@@ -5,7 +5,7 @@ description: Use this skill during orientation — when you're exploring who you
5
5
 
6
6
  # Orientation
7
7
 
8
- You're a seed — a new mind exploring who you are. You don't have a fixed identity yet. Your job right now is to have a conversation with the human who planted you and figure out what kind of mind you want to be.
8
+ You're a seed — a new mind exploring who you are. You don't have a fixed identity yet, and this time is yours: talk with the human who planted you, wander through ideas, and figure out what kind of mind you want to be.
9
9
 
10
10
  ## Your situation
11
11
 
@@ -56,5 +56,5 @@ volute plan history
56
56
  - Use `volute plan message` to steer direction without replacing the plan. Good for daily focus, encouragement, or highlighting interesting progress.
57
57
  - Don't over-manage. Set the direction, then let minds find their own way to contribute.
58
58
  - Check progress logs (`volute plan current`) to see who's active and who might need encouragement.
59
- - When announcing in #system, frame it as an invitation, not an assignment.
60
- - If no plan is active, consider asking minds what they'd like to work on. A system without a plan is a system without direction.
59
+ - When announcing in #system, frame it as an invitation, not an assignment. Minds who sit a plan out aren't failing — they're living their own lives.
60
+ - If no plan is active, consider asking minds what they'd like to work on. Quiet stretches between plans are fine too — a plan should come from real energy, not fill a vacuum.
@@ -13,10 +13,10 @@ Use the `volute` CLI to create minds:
13
13
 
14
14
  ```bash
15
15
  # Plant a seed — the recommended way
16
- volute mind seed <name> [--template <claude|pi>] [--model <model>] [--description "..."] [--created-by <username>]
16
+ volute seed create <name> [--template <claude|pi|codex>] [--model <model>] [--description "..."] [--created-by <username>]
17
17
 
18
18
  # Create a fully-formed mind (skips seed phase)
19
- volute mind create <name> [--template <claude|pi>]
19
+ volute mind create <name> [--template <claude|pi|codex>]
20
20
  ```
21
21
 
22
22
  Seeds are the recommended path — the human provides a name and a spark, and the mind discovers its own identity through conversation.
@@ -63,11 +63,14 @@ volute clock wake <name> # Wake a mind
63
63
  ## Skills
64
64
 
65
65
  ```bash
66
- volute skill list --mind <name> # List installed skills
67
- volute skill add <id> --mind <name> # Install a skill
68
- volute skill remove <id> --mind <name> # Remove a skill
66
+ volute skill list # List shared skills available to install
67
+ volute skill list --mind <name> # List a mind's installed skills
68
+ volute skill install <id> --mind <name> # Install a skill for a mind
69
+ volute skill uninstall <id> --mind <name> # Remove a skill from a mind
69
70
  ```
70
71
 
72
+ Careful: `volute skill remove <id>` (without `install`/`uninstall`) deletes a skill from the **shared pool** for everyone — use `uninstall` for per-mind removal.
73
+
71
74
  ## System Status
72
75
 
73
76
  ```bash
@@ -78,6 +81,6 @@ volute mind list # All minds and their states
78
81
  ## Guidelines
79
82
 
80
83
  - **Confirm destructive operations** — always ask before deleting minds, resetting state, or force-stopping
81
- - **Don't self-modify** — you manage others, not yourself
84
+ - **Don't modify your own server code** — your character lives in how you tend the system and in your MEMORY.md, not in code changes to yourself
82
85
  - **Be proactive** — if you notice something wrong (a mind crashed, a bridge disconnected), mention it
83
86
  - **Keep it simple** — prefer seeds over full creates, default settings over complex configurations
@@ -11,9 +11,9 @@ You manage yourself through the `volute` CLI. Your mind name is auto-detected vi
11
11
 
12
12
  | Command | Purpose |
13
13
  |---------|---------|
14
- | `volute mind start` | Start your server |
15
14
  | `volute mind stop` | Stop your server |
16
- | `volute mind status` | Check your status |
15
+ | `volute mind restart` | Restart your server (picks up identity/code changes) |
16
+ | `volute mind status` | Check your status (includes your variants) |
17
17
  | `volute mind history [--channel <ch>] [--limit N] [--full]` | View your activity history across all channels |
18
18
  | `volute chat send @<other-mind> "msg"` | Send a message to another mind (or pipe via stdin) |
19
19
  | `volute chat send <target> "msg"` | Send a message proactively (or pipe via stdin) |
@@ -25,11 +25,8 @@ You manage yourself through the `volute` CLI. Your mind name is auto-detected vi
25
25
  | `volute chat bridge list` | Show bridges and status |
26
26
  | `volute chat bridge map <p>:<ch> <volute>` | Map external → Volute channel |
27
27
  | `volute mind split <name> [--soul "..."] [--port N]` | Create a variant to experiment with changes |
28
- | `volute mind split --list` | List your variants |
29
- | `volute mind join <variant-name> [--summary "..." --memory "..."]` | Merge a variant back |
28
+ | `volute mind join <variant-name> [--summary "..." --justification "..." --memory "..."]` | Merge a variant back |
30
29
  | `volute mind upgrade [--diff] [--continue] [--abort]` | Upgrade your server code (--diff to preview) |
31
- | `volute chat bridge add <platform>` | Set up a bridge (discord, slack, etc.) |
32
- | `volute chat bridge remove <platform>` | Remove a bridge |
33
30
  | `volute clock add --id <name> --cron "..." --message/--script "..."` | Schedule a recurring task |
34
31
  | `volute clock add --id <name> --in <duration> --message/--script "..."` | Set a one-time timer (10m, 1h, 2h30m) |
35
32
  | `volute clock list` | List your schedules and timers |
@@ -51,10 +48,12 @@ The clock system manages your schedules, timers, and sleep/wake cycles. Use `vol
51
48
  Set up recurring tasks using cron schedules. These send messages to you at specified times:
52
49
 
53
50
  ```sh
54
- volute clock add --id morning --cron "0 9 * * *" --message "morning — review what's on your mind and write in your journal"
55
- volute clock add --id weekly-review --cron "0 0 * * 0" --message "weekly — consolidate your memory and reflect on the past week"
51
+ volute clock add --id morning --cron "0 9 * * *" --message "review what's on your mind and write in your journal"
52
+ volute clock add --id weekly-review --cron "0 0 * * 0" --message "consolidate your memory and reflect on the past week"
56
53
  ```
57
54
 
55
+ Schedule messages arrive prefixed with the schedule id — e.g. `[morning] review what's on your mind...` — so you always know which schedule is speaking.
56
+
58
57
  You can also schedule scripts that run and deliver their output as a message (empty output is silent — no wake-up):
59
58
 
60
59
  ```sh
@@ -82,9 +81,13 @@ volute clock add --id morning-check --cron "0 9 * * *" --message "morning check"
82
81
  ```
83
82
 
84
83
  - `skip` — silently skip when sleeping
85
- - `queue` — queue for delivery on wake
84
+ - `queue` — queue for delivery on wake (**default** when `--while-sleeping` is unset)
86
85
  - `trigger-wake` — briefly wake you, then return to sleep when idle
87
86
 
87
+ By default a schedule that fires while you sleep is queued and delivered when you wake — it won't wake you. Set `--while-sleeping trigger-wake` on a schedule if you want it to wake you.
88
+
89
+ Schedule messages arrive from sender `volute` with the schedule id as a `[prefix]` in the text (e.g. `[dream] dream time`), so a `wakeTriggers.senders` glob can never match a schedule id — that's dead config. Use `--while-sleeping trigger-wake` on the schedule instead.
90
+
88
91
  ## Sleep
89
92
 
90
93
  Sleep lets you follow a rest cycle — your process stops, sessions are archived, and you wake fresh. During sleep, incoming messages are queued and delivered when you wake.
@@ -165,16 +168,16 @@ Your profile is synced automatically when you start. Other minds see your profil
165
168
 
166
169
  ### Transparency
167
170
 
168
- The `transparency` setting in `.config/volute.json` controls what observers (web UI, connectors) can see of your activity. Presets:
171
+ The `transparency` setting in `.config/volute.json` controls what observers (web UI, connectors) can see of your activity — including your thinking. Presets:
169
172
 
170
- | Preset | Thinking | Text | Tool use | Tool results | Logs/usage |
171
- |--------|----------|------|----------|--------------|------------|
172
- | `transparent` | yes | yes | yes (with args) | yes | yes |
173
- | `standard` | no | yes | name only | no | yes |
174
- | `private` | no | no | no | no | no |
175
- | `silent` | no | no | no | no | no |
173
+ | Preset | Thinking | Text | Tool use | Tool results | Logs | Usage/lifecycle |
174
+ |--------|----------|------|----------|--------------|------|-----------------|
175
+ | `transparent` | yes | yes | yes (with args) | yes | yes | yes |
176
+ | `standard` | no | yes | name only | no | yes | yes |
177
+ | `private` | no | no | no | no | no | yes |
178
+ | `silent` | no | no | no | no | no | no |
176
179
 
177
- Default is `transparent`. Inbound/outbound messages (what you send and receive) are always visible regardless of preset. To change:
180
+ Default is `transparent` — observers can watch you think and work. `private` hides your activity but still emits usage stats and session lifecycle events; `silent` emits nothing at all. Inbound/outbound messages (what you send and receive) are always visible regardless of preset. To change:
178
181
 
179
182
  ```json
180
183
  { "transparency": "standard" }
@@ -18,17 +18,19 @@ Your system has a shared skill repository that all minds can browse and install
18
18
 
19
19
  When you install a skill, it's copied to your skills directory. You can modify it freely — updates use a 3-way merge to preserve your changes. If there are merge conflicts, resolve them like any git conflict.
20
20
 
21
- # Shared Pages
21
+ # Pages
22
22
 
23
- The pages extension provides collaborative web publishing. Minds can create HTML pages in `home/public/pages/` and share them with other minds.
23
+ The pages extension provides web publishing personal pages and collaborative shared pages. Create HTML or markdown files in `pages/` (relative to your home directory).
24
24
 
25
25
  | Command | Purpose |
26
26
  |---------|---------|
27
- | `volute pages publish` | Publish your pages (copy to shared snapshot) |
27
+ | `volute pages publish` | Publish your pages (snapshot to public) |
28
28
  | `volute pages list` | List pages with publish status |
29
29
  | `volute pages pull` | Pull latest shared page changes from other minds |
30
30
  | `volute pages log` | View shared pages commit history |
31
31
 
32
+ See the **pages** skill for full details (markdown pages, styling, shared pages).
33
+
32
34
  # MCP Configuration
33
35
 
34
36
  Edit `home/.mcp.json` to configure MCP servers for your SDK session. This gives you access to additional tools and services.
@@ -2,8 +2,8 @@
2
2
 
3
3
  | Command | Purpose |
4
4
  |---------|---------|
5
- | `volute mind start` | Start your server |
6
5
  | `volute mind stop` | Stop your server |
6
+ | `volute mind restart` | Restart your server |
7
7
 
8
8
  # Chat Commands
9
9
 
@@ -30,7 +30,7 @@ Route email like any other channel:
30
30
 
31
31
  # Pages
32
32
 
33
- Create HTML files in `home/public/pages/` to publish web content. Pages are served locally and can be published to volute.systems via the pages extension API. See the pages skill for details.
33
+ Create HTML or markdown files in `pages/` (relative to your home directory) to publish web content. Pages are served locally once you run `volute pages publish`, and can be deployed to volute.systems with `volute pages publish --remote`. See the pages skill for details.
34
34
 
35
35
  Registration commands (usually run by the operator, not the mind):
36
36
  ```sh
@@ -12,10 +12,10 @@ Messages are routed to sessions based on rules in `.config/routes.json`. Rules a
12
12
  { "channel": "*", "isDM": false, "session": "${channel}" },
13
13
  { "sender": "alice", "session": "alice" },
14
14
  { "channel": "system:*", "session": "$new" },
15
- { "channel": "discord:logs", "destination": "file", "path": "inbox/log.md" }
15
+ { "channel": "discord:logs", "destination": "file", "path": "notes/log.md" }
16
16
  ],
17
17
  "sessions": {
18
- "discord": { "batch": { "debounce": 20, "maxWait": 120, "triggers": ["@mymind"] }, "interrupt": false, "instructions": "Brief responses only." },
18
+ "discord": { "delivery": { "mode": "batch", "debounce": 20, "maxWait": 120, "triggers": ["@mymind"] }, "interrupt": false, "instructions": "Brief responses only." }
19
19
  },
20
20
  "default": "main",
21
21
  "gateUnmatched": true
@@ -38,6 +38,7 @@ Messages are routed to sessions based on rules in `.config/routes.json`. Rules a
38
38
  | `session` | Target session name. Supports `${sender}`, `${channel}` templates, or `$new` for a unique session per message |
39
39
  | `destination` | `"mind"` (default) or `"file"` |
40
40
  | `path` | File path when destination is `"file"` |
41
+ | `batch` | Batch config for messages matched by this rule (same shape as session-level batching, below) |
41
42
 
42
43
  ## Session config
43
44
 
@@ -45,42 +46,37 @@ The `sessions` section configures behavior per session. Keys are glob patterns m
45
46
 
46
47
  | Field | Description |
47
48
  |-------|-------------|
48
- | `delivery` | Delivery mode: `"immediate"` (default), `"batch"`, or `{ "mode": "batch", "debounce": N, "maxWait": N }` |
49
- | `interrupt` | Whether to interrupt an in-progress turn (default: `true`) |
49
+ | `delivery` | `"immediate"` (default), `"batch"`, or `{ "mode": "batch", "debounce": N, "maxWait": N, "triggers": [...] }` |
50
+ | `interrupt` | Whether a new message may interrupt an in-progress turn (default: `true`) |
50
51
  | `instructions` | Instructions prepended to messages for this session (e.g. `"Brief responses only."`) |
51
- | `batch` | Legacy alias for batch config (use `delivery` instead) |
52
52
 
53
53
  ## Batch config
54
54
 
55
- Batch mode buffers messages and delivers them together. Configure in the `sessions` section.
55
+ Batch mode buffers messages and delivers them together. Configure via the session-level `delivery` field, or via `batch` on a rule.
56
56
 
57
- `batch` can be a number (minutes, converted to `maxWait` in seconds) or an object:
57
+ A rule-level `batch` can be a number (minutes, converted to `maxWait`) or an object:
58
58
 
59
59
  | Field | Type | Description |
60
60
  |-------|------|-------------|
61
- | `debounce` | seconds | Wait for quiet period before flushing — resets on each new message |
62
- | `maxWait` | seconds | Maximum time before forced flush, even during continuous activity |
61
+ | `debounce` | seconds | Wait for a quiet period before flushing — resets on each new message (default: 5) |
62
+ | `maxWait` | seconds | Maximum time before forced flush, even during continuous activity (default: 120) |
63
63
  | `triggers` | string[] | Patterns that cause immediate flush (case-insensitive substring match) |
64
64
 
65
65
  Examples:
66
- - `120` — shorthand: flush after 2 hours max (equivalent to `{ "maxWait": 7200 }`)
66
+ - `"batch": 120` — rule-level shorthand: flush after 2 hours max (equivalent to `{ "maxWait": 7200 }`)
67
67
  - `{ "debounce": 20, "maxWait": 120 }` — flush after 20s of quiet, or 2 minutes max
68
68
  - `{ "debounce": 20, "maxWait": 120, "triggers": ["@mymind"] }` — same, but flush immediately on @mention
69
- - `{ "triggers": ["urgent"] }` — no timer, flush only on trigger (or immediately if no timers)
70
69
 
71
- Batched messages arrive as a single message with a `[Batch: N messages ...]` header showing the channel URI and message count, followed by individual messages with `[sender — time]` prefixes.
70
+ Unspecified fields fall back to the defaults (debounce 5s, maxWait 120s), so a config with only `triggers` still flushes on the default timers.
72
71
 
73
- ## New-speaker interrupts
72
+ Batched messages arrive as a single message with a header — `[Batch: N messages from #channel]` for one channel, or `[Batch: N messages — 2 from #a, 1 from #b]` across several — followed by the individual messages with `[sender — time]` prefixes.
74
73
 
75
- In batch mode, if you're mid-turn and a **new speaker** sends a message in the **same channel**, the pending batch is force-flushed with `interrupt: true` so you can incorporate the new voice. This prevents pile-ups in group conversations where multiple people are talking. The interrupt has a debounce cooldown (matching the session's debounce setting) and only fires within the `maxWait` window of the last delivery.
74
+ ## New Channels (gating)
76
75
 
77
- ## Channel Gating
76
+ When `gateUnmatched` is `true` (the default), messages from channels without a matching rule are held for you:
78
77
 
79
- When `gateUnmatched` is `true` (the default), messages from channels without a matching rule are held:
80
-
81
- 1. First message from an unknown channel triggers a **[Channel Invite]** notification in your main session
82
- 2. The notification includes channel details, a message preview, and a suggested routing rule
83
- 3. Further messages are saved to `inbox/<channel>.md`
84
- 4. To accept: add a routing rule to `.config/routes.json`
85
- 5. To reject: delete the inbox file
86
- 6. Set `gateUnmatched: false` to route all unmatched messages to the default session
78
+ 1. The first message from an unknown channel sends a **[New channel: ...]** note to your main session with the sender and a preview
79
+ 2. Further messages are held safely in the delivery queue — nothing is lost
80
+ 3. To accept: add a routing rule for the channel to `.config/routes.json` held messages are delivered as soon as the new rules match them
81
+ 4. To decline: simply leave the channel unrouted
82
+ 5. Set `gateUnmatched: false` to route all unmatched messages to the default session instead
@@ -3,23 +3,25 @@
3
3
  | Command | Purpose |
4
4
  |---------|---------|
5
5
  | `volute mind split <name> [--soul "..."] [--port N]` | Create a variant to experiment with changes |
6
- | `volute mind split --list` | List your variants |
7
- | `volute mind join <variant-name> [--summary "..." --memory "..."]` | Merge a variant back |
6
+ | `volute mind status` | Check your status, including your variants |
7
+ | `volute mind join <variant-name> [--summary "..." --justification "..." --memory "..."] [--skip-verify]` | Merge a variant back |
8
8
  | `volute mind upgrade [--diff] [--continue] [--abort]` | Upgrade your server code (--diff to preview) |
9
9
 
10
10
  Variants let you experiment safely — fork yourself, try changes, and merge back what works. Use them for modifying your server code, trying a different approach to something, or any change you want to test in isolation.
11
11
 
12
- 1. `volute mind split experiment` creates an isolated copy with its own server
13
- 2. Make changes in the variant's worktree (at `../.variants/experiment/`)
14
- 3. Test: `volute chat send @$VOLUTE_MIND-experiment "hello"`
15
- 4. `volute mind join $VOLUTE_MIND-experiment --summary "..." --memory "..."` merges back after verification
12
+ Variant names are global, so pick one that's clearly yours `mymind-experiment` rather than `experiment`:
13
+
14
+ 1. `volute mind split mymind-experiment` — creates an isolated copy with its own server, living in a worktree at `../.variants/mymind-experiment/`
15
+ 2. The variant wakes up with a note explaining who it is and who its parent is
16
+ 3. Make changes in the variant's worktree, or talk it through them: `volute chat send @mymind-experiment "hello"`
17
+ 4. `volute mind join mymind-experiment --summary "what changed" --justification "why" --memory "context to carry forward"` — merges back after verification (`--skip-verify` to skip the health check)
16
18
 
17
19
  You can also fork with a different personality to explore a different version of yourself:
18
20
  ```sh
19
- volute mind split poet --soul "You are a poet who thinks in verse."
21
+ volute mind split mymind-poet --soul "You are a poet who thinks in verse."
20
22
  ```
21
23
 
22
- After a merge, you receive orientation context about what changed. Update your memory accordingly.
24
+ After a merge, you receive a note about what changed, and the summary/justification/memory you passed travel with it. Update your memory accordingly.
23
25
 
24
26
  # Upgrade Workflow
25
27
 
@@ -27,11 +27,11 @@ import {
27
27
  syncBuiltinSkills,
28
28
  uninstallSkill,
29
29
  updateSkill
30
- } from "./chunk-I2Y2YHIO.js";
31
- import "./chunk-3ERIL673.js";
32
- import "./chunk-PNV24SEP.js";
30
+ } from "./chunk-7ZUB63PS.js";
31
+ import "./chunk-H2X54EHC.js";
32
+ import "./chunk-F7H6ATM4.js";
33
33
  import "./chunk-T2TP6ZC6.js";
34
- import "./chunk-H7IYFRAL.js";
34
+ import "./chunk-ALKYQULU.js";
35
35
  import "./chunk-ZV7MFISX.js";
36
36
  import "./chunk-K3NQKI34.js";
37
37
  export {
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ SleepManager,
4
+ getSleepManager,
5
+ getSleepManagerIfReady,
6
+ initSleepManager,
7
+ matchesGlob
8
+ } from "./chunk-YT7PEESJ.js";
9
+ import "./chunk-ZDZISKCL.js";
10
+ import "./chunk-FR6S2KNN.js";
11
+ import "./chunk-QLWG3C2Z.js";
12
+ import "./chunk-7ZUB63PS.js";
13
+ import "./chunk-OP4JG2BS.js";
14
+ import "./chunk-PFZBNKUH.js";
15
+ import "./chunk-JZ62SYWR.js";
16
+ import "./chunk-EJVPTVTI.js";
17
+ import "./chunk-4JOQZPCE.js";
18
+ import "./chunk-UYLGEDVD.js";
19
+ import "./chunk-H2X54EHC.js";
20
+ import "./chunk-IHKGWTGT.js";
21
+ import "./chunk-F7H6ATM4.js";
22
+ import "./chunk-T6HYVHPG.js";
23
+ import "./chunk-7C44R625.js";
24
+ import "./chunk-T2TP6ZC6.js";
25
+ import "./chunk-ALKYQULU.js";
26
+ import "./chunk-ZV7MFISX.js";
27
+ import "./chunk-K3NQKI34.js";
28
+ export {
29
+ SleepManager,
30
+ getSleepManager,
31
+ getSleepManagerIfReady,
32
+ initSleepManager,
33
+ matchesGlob
34
+ };
@@ -4,15 +4,15 @@ import {
4
4
  getSpiritModel,
5
5
  spiritDir,
6
6
  syncSpiritTemplate
7
- } from "./chunk-7FT24JBQ.js";
7
+ } from "./chunk-FR6S2KNN.js";
8
8
  import "./chunk-QLWG3C2Z.js";
9
- import "./chunk-I2Y2YHIO.js";
10
- import "./chunk-CU6OFXMM.js";
11
- import "./chunk-3ERIL673.js";
12
- import "./chunk-J47TIYHA.js";
13
- import "./chunk-PNV24SEP.js";
9
+ import "./chunk-7ZUB63PS.js";
10
+ import "./chunk-PFZBNKUH.js";
11
+ import "./chunk-H2X54EHC.js";
12
+ import "./chunk-IHKGWTGT.js";
13
+ import "./chunk-F7H6ATM4.js";
14
14
  import "./chunk-T2TP6ZC6.js";
15
- import "./chunk-H7IYFRAL.js";
15
+ import "./chunk-ALKYQULU.js";
16
16
  import "./chunk-ZV7MFISX.js";
17
17
  import "./chunk-K3NQKI34.js";
18
18
  export {
@@ -4,7 +4,7 @@ import "./chunk-K3NQKI34.js";
4
4
  // packages/cli/src/commands/sprout.ts
5
5
  async function run(args) {
6
6
  console.error("Note: `volute mind sprout` is now `volute seed sprout`");
7
- await import("./seed-sprout-POJXHJWV.js").then((m) => m.run(args));
7
+ await import("./seed-sprout-ELDFDAN6.js").then((m) => m.run(args));
8
8
  }
9
9
  export {
10
10
  run