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
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ publish
4
+ } from "./chunk-UU7A7KLB.js";
5
+ import {
6
+ logger_default
7
+ } from "./chunk-YUIHSKR6.js";
8
+
9
+ // src/lib/mind-activity-tracker.ts
10
+ var IDLE_TIMEOUT_MS = 2 * 60 * 1e3;
11
+ var minds = /* @__PURE__ */ new Map();
12
+ function getState(mind) {
13
+ let state = minds.get(mind);
14
+ if (!state) {
15
+ state = { active: false, idleTimer: null };
16
+ minds.set(mind, state);
17
+ }
18
+ return state;
19
+ }
20
+ var IGNORED_EVENTS = /* @__PURE__ */ new Set(["done", "usage", "log"]);
21
+ function onMindEvent(mind, type, channel) {
22
+ const state = getState(mind);
23
+ if (type === "done") {
24
+ state.active = false;
25
+ if (state.idleTimer) {
26
+ clearTimeout(state.idleTimer);
27
+ }
28
+ state.idleTimer = setTimeout(() => {
29
+ state.idleTimer = null;
30
+ if (!state.active) {
31
+ publish({
32
+ type: "mind_idle",
33
+ mind,
34
+ summary: `${mind} is idle`
35
+ }).catch((err) => {
36
+ logger_default.error("[mind-activity] failed to publish mind_idle", logger_default.errorData(err));
37
+ });
38
+ }
39
+ }, IDLE_TIMEOUT_MS);
40
+ } else if (!IGNORED_EVENTS.has(type)) {
41
+ if (state.idleTimer) {
42
+ clearTimeout(state.idleTimer);
43
+ state.idleTimer = null;
44
+ }
45
+ if (!state.active) {
46
+ state.active = true;
47
+ state.channel = channel;
48
+ publish({
49
+ type: "mind_active",
50
+ mind,
51
+ summary: `${mind} is active`,
52
+ metadata: channel ? { channel } : void 0
53
+ }).catch((err) => {
54
+ logger_default.error("[mind-activity] failed to publish mind_active", logger_default.errorData(err));
55
+ });
56
+ }
57
+ }
58
+ }
59
+ function markIdle(mind) {
60
+ const state = minds.get(mind);
61
+ if (!state) return;
62
+ if (state.idleTimer) {
63
+ clearTimeout(state.idleTimer);
64
+ state.idleTimer = null;
65
+ }
66
+ if (state.active) {
67
+ state.active = false;
68
+ publish({
69
+ type: "mind_idle",
70
+ mind,
71
+ summary: `${mind} is idle`
72
+ }).catch((err) => {
73
+ logger_default.error("[mind-activity] failed to publish mind_idle", logger_default.errorData(err));
74
+ });
75
+ }
76
+ minds.delete(mind);
77
+ }
78
+ function getActiveMinds() {
79
+ const result = [];
80
+ for (const [mind, state] of minds) {
81
+ if (state.active) result.push(mind);
82
+ }
83
+ return result;
84
+ }
85
+ function stopAll() {
86
+ for (const [, state] of minds) {
87
+ if (state.idleTimer) clearTimeout(state.idleTimer);
88
+ }
89
+ minds.clear();
90
+ }
91
+
92
+ export {
93
+ onMindEvent,
94
+ markIdle,
95
+ getActiveMinds,
96
+ stopAll
97
+ };
@@ -4,10 +4,10 @@ import {
4
4
  } from "./chunk-YUIHSKR6.js";
5
5
  import {
6
6
  getDb
7
- } from "./chunk-Z524RFCJ.js";
7
+ } from "./chunk-5XNT2472.js";
8
8
  import {
9
9
  sharedSkills
10
- } from "./chunk-VQWDC6UK.js";
10
+ } from "./chunk-NSE7VJQA.js";
11
11
  import {
12
12
  exec,
13
13
  gitExec
@@ -6,6 +6,7 @@ import {
6
6
  // src/lib/schema.ts
7
7
  var schema_exports = {};
8
8
  __export(schema_exports, {
9
+ activity: () => activity,
9
10
  conversationParticipants: () => conversationParticipants,
10
11
  conversations: () => conversations,
11
12
  deliveryQueue: () => deliveryQueue,
@@ -115,6 +116,21 @@ var deliveryQueue = sqliteTable(
115
116
  index("idx_delivery_queue_mind_status").on(table.mind, table.status)
116
117
  ]
117
118
  );
119
+ var activity = sqliteTable(
120
+ "activity",
121
+ {
122
+ id: integer("id").primaryKey({ autoIncrement: true }),
123
+ type: text("type").notNull(),
124
+ mind: text("mind").notNull(),
125
+ summary: text("summary").notNull(),
126
+ metadata: text("metadata"),
127
+ created_at: text("created_at").notNull().default(sql`(datetime('now'))`)
128
+ },
129
+ (table) => [
130
+ index("idx_activity_created_at").on(table.created_at),
131
+ index("idx_activity_mind").on(table.mind)
132
+ ]
133
+ );
118
134
  var messages = sqliteTable(
119
135
  "messages",
120
136
  {
@@ -137,6 +153,7 @@ export {
137
153
  systemPrompts,
138
154
  sharedSkills,
139
155
  deliveryQueue,
156
+ activity,
140
157
  messages,
141
158
  schema_exports
142
159
  };
@@ -31,7 +31,8 @@ function readJson(path) {
31
31
  if (!existsSync(path)) return null;
32
32
  try {
33
33
  return JSON.parse(readFileSync(path, "utf-8"));
34
- } catch {
34
+ } catch (err) {
35
+ console.error(`[volute-config] failed to parse ${path}: ${err}`);
35
36
  return null;
36
37
  }
37
38
  }
@@ -7,10 +7,10 @@ import {
7
7
  } from "./chunk-VDWCHYTS.js";
8
8
  import {
9
9
  getDb
10
- } from "./chunk-Z524RFCJ.js";
10
+ } from "./chunk-5XNT2472.js";
11
11
  import {
12
12
  systemPrompts
13
- } from "./chunk-VQWDC6UK.js";
13
+ } from "./chunk-NSE7VJQA.js";
14
14
  import {
15
15
  chownMindDir,
16
16
  isIsolationEnabled,
@@ -505,6 +505,12 @@ var MindManager = class {
505
505
  this.minds.delete(name);
506
506
  if (this.shuttingDown || this.stopping.has(name)) return;
507
507
  mlog.error(`mind ${name} exited with code ${code}`);
508
+ import("./mind-activity-tracker-624QLQLC.js").then(({ markIdle }) => markIdle(name)).catch(() => {
509
+ });
510
+ import("./activity-events-OMXKXD5N.js").then(
511
+ ({ publish }) => publish({ type: "mind_stopped", mind: name, summary: `${name} crashed (exit ${code})` })
512
+ ).catch(() => {
513
+ });
508
514
  const { shouldRestart, delay, attempt } = this.restartTracker.recordCrash(name);
509
515
  this.saveCrashAttempts();
510
516
  if (!shouldRestart) {
@@ -15,7 +15,7 @@ import {
15
15
 
16
16
  // src/lib/shared.ts
17
17
  import { execFileSync } from "child_process";
18
- import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
18
+ import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
19
19
  import { resolve } from "path";
20
20
  function readWorktreeGitDir(worktreePath) {
21
21
  const dotGit = resolve(worktreePath, ".git");
@@ -34,7 +34,20 @@ function sharedDir() {
34
34
  async function ensureSharedRepo() {
35
35
  const dir = sharedDir();
36
36
  mkdirSync(dir, { recursive: true });
37
- if (existsSync(resolve(dir, ".git"))) return;
37
+ if (existsSync(resolve(dir, ".git"))) {
38
+ try {
39
+ await gitExec(["rev-parse", "HEAD"], { cwd: dir });
40
+ return;
41
+ } catch (err) {
42
+ const msg = err instanceof Error ? err.message : String(err);
43
+ if (msg.includes("unknown revision") || msg.includes("bad default revision")) {
44
+ logger_default.warn("shared repo has no commits, re-initializing");
45
+ rmSync(resolve(dir, ".git"), { recursive: true, force: true });
46
+ } else {
47
+ throw err;
48
+ }
49
+ }
50
+ }
38
51
  const initArgs = isIsolationEnabled() ? ["init", "--shared=group"] : ["init"];
39
52
  await gitExec(initArgs, { cwd: dir });
40
53
  await gitExec(["checkout", "-b", "main"], { cwd: dir });
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ logger_default
4
+ } from "./chunk-YUIHSKR6.js";
5
+ import {
6
+ getDb
7
+ } from "./chunk-5XNT2472.js";
8
+ import {
9
+ activity
10
+ } from "./chunk-NSE7VJQA.js";
11
+
12
+ // src/lib/activity-events.ts
13
+ var subscribers = /* @__PURE__ */ new Set();
14
+ function subscribe(callback) {
15
+ subscribers.add(callback);
16
+ return () => {
17
+ subscribers.delete(callback);
18
+ };
19
+ }
20
+ async function publish(event) {
21
+ const created_at = event.created_at ?? (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
22
+ let id = 0;
23
+ try {
24
+ const db = await getDb();
25
+ const result = await db.insert(activity).values({
26
+ type: event.type,
27
+ mind: event.mind,
28
+ summary: event.summary,
29
+ metadata: event.metadata ? JSON.stringify(event.metadata) : null,
30
+ created_at
31
+ });
32
+ id = Number(result.lastInsertRowid);
33
+ } catch (err) {
34
+ logger_default.error("[activity-events] failed to persist activity", logger_default.errorData(err));
35
+ }
36
+ const full = { ...event, id, created_at };
37
+ notify(full);
38
+ }
39
+ function broadcast(event) {
40
+ const created_at = event.created_at ?? (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
41
+ notify({ ...event, id: 0, created_at });
42
+ }
43
+ function notify(event) {
44
+ for (const cb of subscribers) {
45
+ try {
46
+ cb(event);
47
+ } catch (err) {
48
+ logger_default.error("[activity-events] subscriber threw:", logger_default.errorData(err));
49
+ subscribers.delete(cb);
50
+ }
51
+ }
52
+ }
53
+
54
+ export {
55
+ subscribe,
56
+ publish,
57
+ broadcast
58
+ };
package/dist/cli.js CHANGED
@@ -9,13 +9,13 @@ if (!process.env.VOLUTE_HOME) {
9
9
  var command = process.argv[2];
10
10
  var args = process.argv.slice(3);
11
11
  if (command === "--version" || command === "-v") {
12
- const { default: pkg } = await import("./package-MYE2ZJLV.js");
12
+ const { default: pkg } = await import("./package-4NHAVUUI.js");
13
13
  console.log(pkg.version);
14
14
  process.exit(0);
15
15
  }
16
16
  switch (command) {
17
17
  case "mind":
18
- await import("./mind-PQ5NCPSU.js").then((m) => m.run(args));
18
+ await import("./mind-Z7CKD6DG.js").then((m) => m.run(args));
19
19
  break;
20
20
  case "send":
21
21
  await import("./send-KVIZIGCE.js").then((m) => m.run(args));
@@ -33,7 +33,7 @@ switch (command) {
33
33
  await import("./channel-PUQKGSQM.js").then((m) => m.run(args));
34
34
  break;
35
35
  case "schedule":
36
- await import("./schedule-LMX7GAQZ.js").then((m) => m.run(args));
36
+ await import("./schedule-FFZG23IW.js").then((m) => m.run(args));
37
37
  break;
38
38
  case "skill":
39
39
  await import("./skill-BCVNI6TV.js").then((m) => m.run(args));
@@ -41,20 +41,23 @@ switch (command) {
41
41
  case "shared":
42
42
  await import("./shared-KO35ZM44.js").then((m) => m.run(args));
43
43
  break;
44
+ case "file":
45
+ await import("./file-C57SK5DK.js").then((m) => m.run(args));
46
+ break;
44
47
  case "env":
45
48
  await import("./env-A3LMO777.js").then((m) => m.run(args));
46
49
  break;
47
50
  case "up":
48
- await import("./up-WG65SWJU.js").then((m) => m.run(args));
51
+ await import("./up-FS7CKM6V.js").then((m) => m.run(args));
49
52
  break;
50
53
  case "down":
51
54
  await import("./down-ZY35KMHR.js").then((m) => m.run(args));
52
55
  break;
53
56
  case "restart":
54
- await import("./daemon-restart-JMZM3QY4.js").then((m) => m.run(args));
57
+ await import("./daemon-restart-KPSWNYTH.js").then((m) => m.run(args));
55
58
  break;
56
59
  case "setup":
57
- await import("./setup-OH3PJUJO.js").then((m) => m.run(args));
60
+ await import("./setup-52YRV7VP.js").then((m) => m.run(args));
58
61
  break;
59
62
  case "service":
60
63
  await import("./service-LUR7WDO7.js").then((m) => m.run(args));
@@ -69,10 +72,10 @@ switch (command) {
69
72
  await import("./seed-J43YDKXG.js").then((m) => m.run(args));
70
73
  break;
71
74
  case "sprout":
72
- await import("./sprout-VBEX63LX.js").then((m) => m.run(args));
75
+ await import("./sprout-QN7Y4VVO.js").then((m) => m.run(args));
73
76
  break;
74
77
  case "pages":
75
- await import("./pages-AXCOSY3P.js").then((m) => m.run(args));
78
+ await import("./pages-4DGQT7ZA.js").then((m) => m.run(args));
76
79
  break;
77
80
  case "register":
78
81
  await import("./register-VSPCMHKX.js").then((m) => m.run(args));
@@ -129,6 +132,13 @@ Commands:
129
132
  volute skill remove <name> Remove a shared skill
130
133
  volute skill uninstall <name> --mind Uninstall a skill from a mind
131
134
 
135
+ volute file send <path> <mind> Send a file to another mind
136
+ volute file list List pending incoming files
137
+ volute file accept <id> Accept a pending file
138
+ volute file reject <id> Reject a pending file
139
+ volute file trust <sender> Trust a sender (auto-deliver)
140
+ volute file untrust <sender> Remove sender trust
141
+
132
142
  volute shared merge "<msg>" Merge shared changes to main
133
143
  volute shared pull Pull latest shared changes
134
144
  volute shared log Show shared repo history
@@ -163,7 +173,7 @@ Options:
163
173
  --version, -v Show version number
164
174
  --help, -h Show this help message
165
175
 
166
- Mind-scoped commands (send, history, variant, connector, schedule, channel, skill, shared, pages)
176
+ Mind-scoped commands (send, history, variant, connector, schedule, channel, file, skill, shared, pages)
167
177
  use --mind <name> or VOLUTE_MIND env var to identify the mind.`);
168
178
  break;
169
179
  default:
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ run
4
+ } from "./chunk-7UFKREVW.js";
2
5
  import {
3
6
  stopDaemon
4
7
  } from "./chunk-7NO7EV5Z.js";
5
- import {
6
- run
7
- } from "./chunk-VE4D3GOP.js";
8
8
  import {
9
9
  getServiceMode,
10
10
  modeLabel,