volute 0.19.0 → 0.20.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 (51) hide show
  1. package/README.md +66 -66
  2. package/dist/activity-events-OMXKXD5N.js +16 -0
  3. package/dist/{chunk-Z524RFCJ.js → chunk-5XNT2472.js} +1 -1
  4. package/dist/{chunk-FGV2H4TX.js → chunk-FGSYHIS3.js} +112 -24
  5. package/dist/chunk-GZ7DW4YL.js +97 -0
  6. package/dist/{chunk-OTWLI7F4.js → chunk-IKMY5X76.js} +2 -2
  7. package/dist/{chunk-VQWDC6UK.js → chunk-NSE7VJQA.js} +17 -0
  8. package/dist/{chunk-EMQSAY3B.js → chunk-O6ASDHFO.js} +2 -1
  9. package/dist/{chunk-2TJGRJ4O.js → chunk-PUVXOZ6T.js} +8 -2
  10. package/dist/{chunk-4KPUF5JD.js → chunk-TIWH32HP.js} +15 -2
  11. package/dist/chunk-UU7A7KLB.js +58 -0
  12. package/dist/cli.js +19 -9
  13. package/dist/{daemon-restart-JMZM3QY4.js → daemon-restart-KPSWNYTH.js} +3 -3
  14. package/dist/daemon.js +1802 -1082
  15. package/dist/{db-5ZVC6MQF.js → db-C2CJ46ZU.js} +2 -2
  16. package/dist/{delivery-manager-ISTJMZDW.js → delivery-manager-CSG7LXA4.js} +3 -3
  17. package/dist/{export-GCDNQCF3.js → export-6QBUOQGC.js} +2 -2
  18. package/dist/file-C57SK5DK.js +204 -0
  19. package/dist/{import-M63VIUJ5.js → import-XEC34Y4Z.js} +1 -1
  20. package/dist/{mind-PQ5NCPSU.js → mind-Z7CKD6DG.js} +2 -2
  21. package/dist/mind-activity-tracker-624QLQLC.js +19 -0
  22. package/dist/{mind-manager-RVCFROAY.js → mind-manager-3DMYKZPB.js} +3 -3
  23. package/dist/{package-MYE2ZJLV.js → package-4NHAVUUI.js} +1 -1
  24. package/dist/{pages-AXCOSY3P.js → pages-4DGQT7ZA.js} +2 -2
  25. package/dist/{publish-YB377JB7.js → publish-TAJUET4I.js} +7 -4
  26. package/dist/{schedule-LMX7GAQZ.js → schedule-FFZG23IW.js} +25 -5
  27. package/dist/{schema-5BW7DFZI.js → schema-GFH6RV3W.js} +3 -1
  28. package/dist/{setup-OH3PJUJO.js → setup-52YRV7VP.js} +16 -0
  29. package/dist/skills/volute-mind/SKILL.md +33 -3
  30. package/dist/{sprout-VBEX63LX.js → sprout-QN7Y4VVO.js} +3 -3
  31. package/dist/{status-JCJAOXTW.js → status-FU2PFVVF.js} +3 -2
  32. package/dist/{up-WG65SWJU.js → up-FS7CKM6V.js} +1 -1
  33. package/dist/web-assets/assets/index-CUZTZzaW.js +64 -0
  34. package/dist/web-assets/assets/index-adVuCkqy.css +1 -0
  35. package/dist/web-assets/index.html +2 -2
  36. package/drizzle/0012_activity.sql +11 -0
  37. package/drizzle/meta/0012_snapshot.json +7 -0
  38. package/drizzle/meta/_journal.json +7 -0
  39. package/package.json +1 -1
  40. package/templates/_base/home/.config/routes.json +2 -2
  41. package/templates/_base/home/VOLUTE.md +1 -1
  42. package/templates/_base/src/lib/daemon-client.ts +22 -0
  43. package/templates/_base/src/lib/transparency.ts +1 -1
  44. package/templates/claude/.init/.config/routes.json +7 -1
  45. package/templates/pi/.init/.config/routes.json +7 -1
  46. package/templates/pi/src/agent.ts +11 -5
  47. package/templates/pi/src/lib/session-context-extension.ts +6 -4
  48. package/templates/pi/src/server.ts +2 -0
  49. package/dist/web-assets/assets/index-BAbuRsVF.css +0 -1
  50. package/dist/web-assets/assets/index-CiQhSKi_.js +0 -63
  51. /package/dist/{chunk-VE4D3GOP.js → chunk-7UFKREVW.js} +0 -0
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getDb
4
- } from "./chunk-Z524RFCJ.js";
5
- import "./chunk-VQWDC6UK.js";
4
+ } from "./chunk-5XNT2472.js";
5
+ import "./chunk-NSE7VJQA.js";
6
6
  import "./chunk-EBGCNDMM.js";
7
7
  import "./chunk-K3NQKI34.js";
8
8
  export {
@@ -3,10 +3,10 @@ import {
3
3
  DeliveryManager,
4
4
  getDeliveryManager,
5
5
  initDeliveryManager
6
- } from "./chunk-FGV2H4TX.js";
6
+ } from "./chunk-FGSYHIS3.js";
7
7
  import "./chunk-YUIHSKR6.js";
8
- import "./chunk-Z524RFCJ.js";
9
- import "./chunk-VQWDC6UK.js";
8
+ import "./chunk-5XNT2472.js";
9
+ import "./chunk-NSE7VJQA.js";
10
10
  import "./chunk-EBGCNDMM.js";
11
11
  import "./chunk-K3NQKI34.js";
12
12
  export {
@@ -59,9 +59,9 @@ async function run(args) {
59
59
  });
60
60
  if (includeHistory) {
61
61
  try {
62
- const { getDb } = await import("./db-5ZVC6MQF.js");
62
+ const { getDb } = await import("./db-C2CJ46ZU.js");
63
63
  const { eq } = await import("drizzle-orm");
64
- const { mindHistory } = await import("./schema-5BW7DFZI.js");
64
+ const { mindHistory } = await import("./schema-GFH6RV3W.js");
65
65
  const db = await getDb();
66
66
  const rows = await db.select().from(mindHistory).where(eq(mindHistory.mind, name));
67
67
  addHistoryToArchive(zip, rows);
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ resolveMindName
4
+ } from "./chunk-NAOW2CLO.js";
5
+ import {
6
+ parseArgs
7
+ } from "./chunk-D424ZQGI.js";
8
+ import {
9
+ daemonFetch
10
+ } from "./chunk-WC6ZHVRL.js";
11
+ import "./chunk-EBGCNDMM.js";
12
+ import "./chunk-K3NQKI34.js";
13
+
14
+ // src/commands/file.ts
15
+ async function run(args) {
16
+ const subcommand = args[0];
17
+ switch (subcommand) {
18
+ case "send":
19
+ await sendFile(args.slice(1));
20
+ break;
21
+ case "list":
22
+ await listPending(args.slice(1));
23
+ break;
24
+ case "accept":
25
+ await acceptFile(args.slice(1));
26
+ break;
27
+ case "reject":
28
+ await rejectFile(args.slice(1));
29
+ break;
30
+ case "trust":
31
+ await trustSender(args.slice(1));
32
+ break;
33
+ case "untrust":
34
+ await untrustSender(args.slice(1));
35
+ break;
36
+ case "--help":
37
+ case "-h":
38
+ case void 0:
39
+ printUsage();
40
+ break;
41
+ default:
42
+ printUsage();
43
+ process.exit(1);
44
+ }
45
+ }
46
+ function printUsage() {
47
+ console.log(`Usage:
48
+ volute file send <path> <target-mind> [--mind <name>] Send a file to another mind
49
+ volute file list [--mind <name>] List pending incoming files
50
+ volute file accept <id> [--mind <name>] Accept a pending file
51
+ volute file reject <id> [--mind <name>] Reject a pending file
52
+ volute file trust <sender> [--mind <name>] Trust a sender (auto-deliver)
53
+ volute file untrust <sender> [--mind <name>] Remove sender trust`);
54
+ }
55
+ async function sendFile(args) {
56
+ const { positional, flags } = parseArgs(args, {
57
+ mind: { type: "string" }
58
+ });
59
+ const mind = resolveMindName(flags);
60
+ const filePath = positional[0];
61
+ const targetMind = positional[1];
62
+ if (!filePath || !targetMind) {
63
+ console.error("Usage: volute file send <path> <target-mind> [--mind <name>]");
64
+ process.exit(1);
65
+ }
66
+ const res = await daemonFetch(`/api/minds/${encodeURIComponent(mind)}/files/send`, {
67
+ method: "POST",
68
+ headers: { "Content-Type": "application/json" },
69
+ body: JSON.stringify({ targetMind, filePath })
70
+ });
71
+ if (!res.ok) {
72
+ const data2 = await res.json();
73
+ console.error(data2.error ?? `Failed to send file: ${res.status}`);
74
+ process.exit(1);
75
+ }
76
+ const data = await res.json();
77
+ if (data.status === "delivered") {
78
+ console.log(`File delivered to ${targetMind}: ${data.destPath}`);
79
+ } else {
80
+ console.log(`File pending approval from ${targetMind} (id: ${data.id})`);
81
+ }
82
+ }
83
+ async function listPending(args) {
84
+ const { flags } = parseArgs(args, {
85
+ mind: { type: "string" }
86
+ });
87
+ const mind = resolveMindName(flags);
88
+ const res = await daemonFetch(`/api/minds/${encodeURIComponent(mind)}/files/pending`);
89
+ if (!res.ok) {
90
+ const data = await res.json();
91
+ console.error(data.error ?? `Failed to list pending files: ${res.status}`);
92
+ process.exit(1);
93
+ }
94
+ const pending = await res.json();
95
+ if (pending.length === 0) {
96
+ console.log("No pending files.");
97
+ return;
98
+ }
99
+ const idW = Math.max(2, ...pending.map((p) => p.id.length));
100
+ const senderW = Math.max(6, ...pending.map((p) => p.sender.length));
101
+ const fileW = Math.max(4, ...pending.map((p) => p.filename.length));
102
+ console.log(`${"ID".padEnd(idW)} ${"SENDER".padEnd(senderW)} ${"FILE".padEnd(fileW)} SIZE`);
103
+ for (const p of pending) {
104
+ console.log(
105
+ `${p.id.padEnd(idW)} ${p.sender.padEnd(senderW)} ${p.filename.padEnd(fileW)} ${formatSize(p.size)}`
106
+ );
107
+ }
108
+ }
109
+ async function acceptFile(args) {
110
+ const { positional, flags } = parseArgs(args, {
111
+ mind: { type: "string" }
112
+ });
113
+ const mind = resolveMindName(flags);
114
+ const id = positional[0];
115
+ if (!id) {
116
+ console.error("Usage: volute file accept <id> [--mind <name>]");
117
+ process.exit(1);
118
+ }
119
+ const res = await daemonFetch(`/api/minds/${encodeURIComponent(mind)}/files/accept`, {
120
+ method: "POST",
121
+ headers: { "Content-Type": "application/json" },
122
+ body: JSON.stringify({ id })
123
+ });
124
+ if (!res.ok) {
125
+ const data2 = await res.json();
126
+ console.error(data2.error ?? `Failed to accept file: ${res.status}`);
127
+ process.exit(1);
128
+ }
129
+ const data = await res.json();
130
+ console.log(`File accepted: ${data.destPath}`);
131
+ }
132
+ async function rejectFile(args) {
133
+ const { positional, flags } = parseArgs(args, {
134
+ mind: { type: "string" }
135
+ });
136
+ const mind = resolveMindName(flags);
137
+ const id = positional[0];
138
+ if (!id) {
139
+ console.error("Usage: volute file reject <id> [--mind <name>]");
140
+ process.exit(1);
141
+ }
142
+ const res = await daemonFetch(`/api/minds/${encodeURIComponent(mind)}/files/reject`, {
143
+ method: "POST",
144
+ headers: { "Content-Type": "application/json" },
145
+ body: JSON.stringify({ id })
146
+ });
147
+ if (!res.ok) {
148
+ const data = await res.json();
149
+ console.error(data.error ?? `Failed to reject file: ${res.status}`);
150
+ process.exit(1);
151
+ }
152
+ console.log(`File rejected: ${id}`);
153
+ }
154
+ async function trustSender(args) {
155
+ const { positional, flags } = parseArgs(args, {
156
+ mind: { type: "string" }
157
+ });
158
+ const mind = resolveMindName(flags);
159
+ const sender = positional[0];
160
+ if (!sender) {
161
+ console.error("Usage: volute file trust <sender> [--mind <name>]");
162
+ process.exit(1);
163
+ }
164
+ const res = await daemonFetch(`/api/minds/${encodeURIComponent(mind)}/files/trust`, {
165
+ method: "POST",
166
+ headers: { "Content-Type": "application/json" },
167
+ body: JSON.stringify({ sender })
168
+ });
169
+ if (!res.ok) {
170
+ const data = await res.json();
171
+ console.error(data.error ?? `Failed to trust sender: ${res.status}`);
172
+ process.exit(1);
173
+ }
174
+ console.log(`Trusted sender: ${sender}`);
175
+ }
176
+ async function untrustSender(args) {
177
+ const { positional, flags } = parseArgs(args, {
178
+ mind: { type: "string" }
179
+ });
180
+ const mind = resolveMindName(flags);
181
+ const sender = positional[0];
182
+ if (!sender) {
183
+ console.error("Usage: volute file untrust <sender> [--mind <name>]");
184
+ process.exit(1);
185
+ }
186
+ const res = await daemonFetch(
187
+ `/api/minds/${encodeURIComponent(mind)}/files/trust/${encodeURIComponent(sender)}`,
188
+ { method: "DELETE" }
189
+ );
190
+ if (!res.ok) {
191
+ const data = await res.json();
192
+ console.error(data.error ?? `Failed to untrust sender: ${res.status}`);
193
+ process.exit(1);
194
+ }
195
+ console.log(`Untrusted sender: ${sender}`);
196
+ }
197
+ function formatSize(bytes) {
198
+ if (bytes < 1024) return `${bytes} B`;
199
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
200
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
201
+ }
202
+ export {
203
+ run
204
+ };
@@ -6,7 +6,7 @@ import {
6
6
  parseNameFromIdentity,
7
7
  run,
8
8
  sessionMatchesWorkspace
9
- } from "./chunk-EMQSAY3B.js";
9
+ } from "./chunk-O6ASDHFO.js";
10
10
  import "./chunk-VDWCHYTS.js";
11
11
  import "./chunk-D424ZQGI.js";
12
12
  import "./chunk-EBGCNDMM.js";
@@ -41,10 +41,10 @@ async function run(args) {
41
41
  await import("./upgrade-AIT24B5I.js").then((m) => m.run(args.slice(1)));
42
42
  break;
43
43
  case "import":
44
- await import("./import-M63VIUJ5.js").then((m) => m.run(args.slice(1)));
44
+ await import("./import-XEC34Y4Z.js").then((m) => m.run(args.slice(1)));
45
45
  break;
46
46
  case "export":
47
- await import("./export-GCDNQCF3.js").then((m) => m.run(args.slice(1)));
47
+ await import("./export-6QBUOQGC.js").then((m) => m.run(args.slice(1)));
48
48
  break;
49
49
  case "--help":
50
50
  case "-h":
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getActiveMinds,
4
+ markIdle,
5
+ onMindEvent,
6
+ stopAll
7
+ } from "./chunk-GZ7DW4YL.js";
8
+ import "./chunk-UU7A7KLB.js";
9
+ import "./chunk-YUIHSKR6.js";
10
+ import "./chunk-5XNT2472.js";
11
+ import "./chunk-NSE7VJQA.js";
12
+ import "./chunk-EBGCNDMM.js";
13
+ import "./chunk-K3NQKI34.js";
14
+ export {
15
+ getActiveMinds,
16
+ markIdle,
17
+ onMindEvent,
18
+ stopAll
19
+ };
@@ -3,11 +3,11 @@ import {
3
3
  MindManager,
4
4
  getMindManager,
5
5
  initMindManager
6
- } from "./chunk-2TJGRJ4O.js";
6
+ } from "./chunk-PUVXOZ6T.js";
7
7
  import "./chunk-YUIHSKR6.js";
8
8
  import "./chunk-VDWCHYTS.js";
9
- import "./chunk-Z524RFCJ.js";
10
- import "./chunk-VQWDC6UK.js";
9
+ import "./chunk-5XNT2472.js";
10
+ import "./chunk-NSE7VJQA.js";
11
11
  import "./chunk-OGXOMR65.js";
12
12
  import "./chunk-EBGCNDMM.js";
13
13
  import "./chunk-K3NQKI34.js";
@@ -4,7 +4,7 @@ import "./chunk-K3NQKI34.js";
4
4
  // package.json
5
5
  var package_default = {
6
6
  name: "volute",
7
- version: "0.19.0",
7
+ version: "0.20.0",
8
8
  description: "CLI for creating and managing self-modifying AI minds powered by the Claude Agent SDK",
9
9
  type: "module",
10
10
  license: "MIT",
@@ -6,10 +6,10 @@ async function run(args) {
6
6
  const subcommand = args[0];
7
7
  switch (subcommand) {
8
8
  case "publish":
9
- await import("./publish-YB377JB7.js").then((m) => m.run(args.slice(1)));
9
+ await import("./publish-TAJUET4I.js").then((m) => m.run(args.slice(1)));
10
10
  break;
11
11
  case "status":
12
- await import("./status-JCJAOXTW.js").then((m) => m.run(args.slice(1)));
12
+ await import("./status-FU2PFVVF.js").then((m) => m.run(args.slice(1)));
13
13
  break;
14
14
  case "--help":
15
15
  case "-h":
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-NAOW2CLO.js";
8
8
  import {
9
9
  sharedDir
10
- } from "./chunk-4KPUF5JD.js";
10
+ } from "./chunk-TIWH32HP.js";
11
11
  import {
12
12
  readSystemsConfig
13
13
  } from "./chunk-FCDU5BFX.js";
@@ -27,17 +27,20 @@ import { existsSync, lstatSync, readdirSync, readFileSync } from "fs";
27
27
  import { relative, resolve } from "path";
28
28
  async function run(args) {
29
29
  const { flags } = parseArgs(args, {
30
- mind: { type: "string" }
30
+ mind: { type: "string" },
31
+ system: { type: "boolean" }
31
32
  });
32
33
  const config = readSystemsConfig();
33
34
  if (!config) {
34
35
  console.error('Not logged in. Run "volute pages register" or "volute pages login" first.');
35
36
  process.exit(1);
36
37
  }
37
- const hasMind = flags.mind || process.env.VOLUTE_MIND;
38
38
  let mindName;
39
39
  let pagesDir;
40
- if (hasMind) {
40
+ if (flags.system) {
41
+ mindName = "system";
42
+ pagesDir = resolve(sharedDir(), "pages");
43
+ } else if (flags.mind || process.env.VOLUTE_MIND) {
41
44
  mindName = resolveMindName(flags);
42
45
  pagesDir = resolve(mindDir(mindName), "home", "pages");
43
46
  } else {
@@ -16,6 +16,7 @@ import "./chunk-EBGCNDMM.js";
16
16
  import "./chunk-K3NQKI34.js";
17
17
 
18
18
  // src/commands/schedule.ts
19
+ import { CronExpressionParser } from "cron-parser";
19
20
  async function run(args) {
20
21
  const subcommand = args[0];
21
22
  switch (subcommand) {
@@ -42,6 +43,7 @@ function printUsage() {
42
43
  console.log(`Usage:
43
44
  volute schedule list [--mind <name>]
44
45
  volute schedule add [--mind <name>] --cron "..." --message "..." [--id name]
46
+ volute schedule add [--mind <name>] --cron "..." --script "..." [--id name]
45
47
  volute schedule remove [--mind <name>] --id <id>`);
46
48
  }
47
49
  async function listSchedules(args) {
@@ -65,10 +67,11 @@ async function listSchedules(args) {
65
67
  }
66
68
  const idW = Math.max(2, ...schedules.map((s) => s.id.length));
67
69
  const cronW = Math.max(4, ...schedules.map((s) => s.cron.length));
68
- console.log(`${"ID".padEnd(idW)} ${"CRON".padEnd(cronW)} ENABLED MESSAGE`);
70
+ const actionLabel = (s) => s.script ? `[script] ${s.script}` : s.message ?? "";
71
+ console.log(`${"ID".padEnd(idW)} ${"CRON".padEnd(cronW)} ENABLED ACTION`);
69
72
  for (const s of schedules) {
70
73
  console.log(
71
- `${s.id.padEnd(idW)} ${s.cron.padEnd(cronW)} ${String(s.enabled).padEnd(7)} ${s.message}`
74
+ `${s.id.padEnd(idW)} ${s.cron.padEnd(cronW)} ${String(s.enabled).padEnd(7)} ${actionLabel(s)}`
72
75
  );
73
76
  }
74
77
  }
@@ -77,14 +80,31 @@ async function addSchedule(args) {
77
80
  mind: { type: "string" },
78
81
  cron: { type: "string" },
79
82
  message: { type: "string" },
83
+ script: { type: "string" },
80
84
  id: { type: "string" }
81
85
  });
82
86
  const mind = resolveMindName(flags);
83
- if (!flags.cron || !flags.message) {
84
- console.error("--cron and --message are required");
87
+ if (!flags.cron) {
88
+ console.error("--cron is required");
85
89
  process.exit(1);
86
90
  }
87
- const body = { cron: flags.cron, message: flags.message };
91
+ if (!flags.message && !flags.script) {
92
+ console.error("--message or --script is required");
93
+ process.exit(1);
94
+ }
95
+ if (flags.message && flags.script) {
96
+ console.error("--message and --script are mutually exclusive");
97
+ process.exit(1);
98
+ }
99
+ try {
100
+ CronExpressionParser.parse(flags.cron);
101
+ } catch {
102
+ console.error(`Invalid cron expression: ${flags.cron}`);
103
+ process.exit(1);
104
+ }
105
+ const body = { cron: flags.cron };
106
+ if (flags.message) body.message = flags.message;
107
+ if (flags.script) body.script = flags.script;
88
108
  if (flags.id) body.id = flags.id;
89
109
  const client = getClient();
90
110
  const res = await daemonFetch(
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ activity,
3
4
  conversationParticipants,
4
5
  conversations,
5
6
  deliveryQueue,
@@ -9,9 +10,10 @@ import {
9
10
  sharedSkills,
10
11
  systemPrompts,
11
12
  users
12
- } from "./chunk-VQWDC6UK.js";
13
+ } from "./chunk-NSE7VJQA.js";
13
14
  import "./chunk-K3NQKI34.js";
14
15
  export {
16
+ activity,
15
17
  conversationParticipants,
16
18
  conversations,
17
19
  deliveryQueue,
@@ -95,6 +95,22 @@ function install(port, host) {
95
95
  console.log(`Created ${MINDS_DIR}`);
96
96
  ensureVoluteGroup({ force: true });
97
97
  console.log("Ensured volute group exists");
98
+ try {
99
+ execFileSync("git", ["config", "--system", "user.name"]);
100
+ console.log("System git identity already configured, skipping");
101
+ } catch {
102
+ try {
103
+ execFileSync("git", ["config", "--system", "user.name", "Volute"]);
104
+ execFileSync("git", ["config", "--system", "user.email", "volute@localhost"]);
105
+ console.log("Configured system git identity");
106
+ } catch (err) {
107
+ const msg = err instanceof Error ? err.message : String(err);
108
+ console.warn(`Warning: failed to set system git config: ${msg}`);
109
+ console.warn("Git commits by the daemon may fail. You can set this manually with:");
110
+ console.warn(' git config --system user.name "Volute"');
111
+ console.warn(' git config --system user.email "volute@localhost"');
112
+ }
113
+ }
98
114
  execFileSync("chmod", ["755", DATA_DIR]);
99
115
  execFileSync("chmod", ["755", MINDS_DIR]);
100
116
  console.log("Set permissions on directories");
@@ -29,7 +29,7 @@ You manage yourself through the `volute` CLI. Your mind name is auto-detected vi
29
29
  | `volute channel list [<platform>]` | List conversations on a platform (or all platforms) |
30
30
  | `volute channel users <platform>` | List users/contacts on a platform |
31
31
  | `volute channel create <platform> --participants u1,u2 [--name "..."]` | Create a conversation on a platform |
32
- | `volute schedule add --cron "..." --message "..."` | Schedule a recurring message to yourself |
32
+ | `volute schedule add --cron "..." --message/--script "..."` | Schedule a recurring task |
33
33
  | `volute schedule list` | List your schedules |
34
34
  | `volute schedule remove --id <id>` | Remove a schedule |
35
35
  | `volute shared status` | See your pending changes vs main |
@@ -46,6 +46,12 @@ volute schedule add --cron "0 9 * * *" --message "morning — review what's on y
46
46
  volute schedule add --cron "0 0 * * 0" --message "weekly — consolidate your memory and reflect on the past week"
47
47
  ```
48
48
 
49
+ You can also schedule scripts that run and deliver their output as a message (empty output is silent — no wake-up):
50
+
51
+ ```sh
52
+ volute schedule add --cron "*/30 * * * *" --script "cat status.txt" --id check-status
53
+ ```
54
+
49
55
  ## Piping Messages via Stdin
50
56
 
51
57
  All send commands accept the message from stdin instead of as an argument. This avoids shell escaping issues with quotes, special characters, and multiline content:
@@ -71,6 +77,23 @@ For group conversations, use `volute channel create volute --participants mind-b
71
77
 
72
78
  Your `.config/volute.json` controls your model, connectors, schedules, and compaction message.
73
79
 
80
+ ### Transparency
81
+
82
+ The `transparency` setting in `.config/volute.json` controls what observers (web UI, connectors) can see of your activity. Presets:
83
+
84
+ | Preset | Thinking | Text | Tool use | Tool results | Logs/usage |
85
+ |--------|----------|------|----------|--------------|------------|
86
+ | `transparent` | yes | yes | yes (with args) | yes | yes |
87
+ | `standard` | no | yes | name only | no | yes |
88
+ | `private` | no | no | no | no | no |
89
+ | `silent` | no | no | no | no | no |
90
+
91
+ Default is `transparent`. Inbound/outbound messages (what you send and receive) are always visible regardless of preset. To change:
92
+
93
+ ```json
94
+ { "transparency": "standard" }
95
+ ```
96
+
74
97
  ## Startup Context
75
98
 
76
99
  Edit `.config/hooks/startup-context.sh` to customize what you see when a new session starts. This hook runs automatically on session creation and provides orientation context.
@@ -132,7 +155,7 @@ Your `shared/` directory is a collaborative space backed by git. Each mind works
132
155
 
133
156
  **Conflicts:** If your merge fails due to conflicts, pull the latest (`volute shared pull`), reconcile the conflicting files, and merge again. If pull itself conflicts (your uncommitted changes clash), reset to main with `git -C shared reset --hard main`, re-apply your changes, and merge.
134
157
 
135
- **Shared pages:** The `shared/pages/` directory is the system-level website. Any mind can contribute. Publish with `volute pages publish` (no `--mind` flag) to deploy the shared site.
158
+ **Shared pages:** The `shared/pages/` directory is the system-level website. Any mind can contribute. Publish with `volute pages publish --system` to deploy the shared site.
136
159
 
137
160
  ## MCP Configuration
138
161
 
@@ -185,9 +208,10 @@ The `sessions` section configures behavior per session. Keys are glob patterns m
185
208
 
186
209
  | Field | Description |
187
210
  |-------|-------------|
188
- | `batch` | Batch config (see below) |
211
+ | `delivery` | Delivery mode: `"immediate"` (default), `"batch"`, or `{ "mode": "batch", "debounce": N, "maxWait": N }` |
189
212
  | `interrupt` | Whether to interrupt an in-progress turn (default: `true`) |
190
213
  | `instructions` | Instructions prepended to messages for this session (e.g. `"Brief responses only."`) |
214
+ | `batch` | Legacy alias for batch config (use `delivery` instead) |
191
215
 
192
216
  ### Batch config
193
217
 
@@ -209,6 +233,10 @@ Examples:
209
233
 
210
234
  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.
211
235
 
236
+ ### New-speaker interrupts
237
+
238
+ 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.
239
+
212
240
  ## Channel Gating
213
241
 
214
242
  When `gateUnmatched` is `true` (the default), messages from channels without a matching rule are held:
@@ -249,7 +277,9 @@ Publish your `home/pages/` directory to the web. Your system must be registered
249
277
 
250
278
  ```sh
251
279
  volute pages publish # publish your pages/ directory
280
+ volute pages publish --system # publish the shared/pages/ system site
252
281
  volute pages status # check your published URL and status
282
+ volute pages status --system # check the system site status
253
283
  ```
254
284
 
255
285
  Your pages are served at `https://{system}.volute.systems/~{your-name}/`. Create an `index.html` in `home/pages/` to get started.
@@ -4,10 +4,10 @@ import {
4
4
  getSharedSkill,
5
5
  installSkill,
6
6
  uninstallSkill
7
- } from "./chunk-OTWLI7F4.js";
7
+ } from "./chunk-IKMY5X76.js";
8
8
  import "./chunk-YUIHSKR6.js";
9
- import "./chunk-Z524RFCJ.js";
10
- import "./chunk-VQWDC6UK.js";
9
+ import "./chunk-5XNT2472.js";
10
+ import "./chunk-NSE7VJQA.js";
11
11
  import "./chunk-DYZGP3EW.js";
12
12
  import "./chunk-OGXOMR65.js";
13
13
  import {
@@ -17,14 +17,15 @@ import "./chunk-K3NQKI34.js";
17
17
  // src/commands/pages/status.ts
18
18
  async function run(args) {
19
19
  const { flags } = parseArgs(args, {
20
- mind: { type: "string" }
20
+ mind: { type: "string" },
21
+ system: { type: "boolean" }
21
22
  });
22
23
  const config = readSystemsConfig();
23
24
  if (!config) {
24
25
  console.error('Not logged in. Run "volute pages register" or "volute pages login" first.');
25
26
  process.exit(1);
26
27
  }
27
- const mindName = resolveMindName(flags);
28
+ const mindName = flags.mind || process.env.VOLUTE_MIND ? resolveMindName(flags) : "system";
28
29
  const res = await systemsFetch(`${config.apiUrl}/api/pages/status/${mindName}`, {
29
30
  headers: { Authorization: `Bearer ${config.apiKey}` }
30
31
  });
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  readGlobalConfig,
4
4
  run
5
- } from "./chunk-VE4D3GOP.js";
5
+ } from "./chunk-7UFKREVW.js";
6
6
  import "./chunk-32VR2EOH.js";
7
7
  import "./chunk-D424ZQGI.js";
8
8
  import "./chunk-DYZGP3EW.js";