volute 0.5.0 → 0.7.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 (62) hide show
  1. package/dist/{agent-Z2B6EFEQ.js → agent-7JF7MT73.js} +13 -9
  2. package/dist/{agent-manager-PXBKA2GK.js → agent-manager-IMZ7ZMBF.js} +4 -4
  3. package/dist/channel-SMCNOIVQ.js +262 -0
  4. package/dist/{chunk-MW2KFO3B.js → chunk-62X577Y7.js} +10 -8
  5. package/dist/chunk-7ACDT3P2.js +265 -0
  6. package/dist/{chunk-MXUCNIBG.js → chunk-BX7KI4S3.js} +68 -3
  7. package/dist/{up-7ILD7GU7.js → chunk-EG45HBSJ.js} +16 -4
  8. package/dist/{chunk-HE67X4T6.js → chunk-H7AMDUIA.js} +1 -1
  9. package/dist/{chunk-7L4AN5D4.js → chunk-JR4UXCTO.js} +1 -1
  10. package/dist/{down-O7IFZLVJ.js → chunk-LLJNZPCU.js} +48 -13
  11. package/dist/{chunk-5X7HGB6L.js → chunk-NKXULRSW.js} +2 -1
  12. package/dist/{chunk-UX25Z2ND.js → chunk-UWHWAPGO.js} +7 -0
  13. package/dist/{chunk-UAVD2AHX.js → chunk-W76KWE23.js} +1 -1
  14. package/dist/chunk-ZZOOTYXK.js +583 -0
  15. package/dist/cli.js +22 -21
  16. package/dist/{connector-LYEMXQEV.js → connector-Y7JPNROO.js} +3 -3
  17. package/dist/connectors/discord.js +38 -7
  18. package/dist/connectors/slack.js +22 -3
  19. package/dist/connectors/telegram.js +34 -4
  20. package/dist/{create-RVCZN6HE.js → create-G525LWEA.js} +2 -2
  21. package/dist/{daemon-client-ZY6UUN2M.js → daemon-client-442IV43D.js} +2 -2
  22. package/dist/daemon-restart-4HVEKYFY.js +23 -0
  23. package/dist/daemon.js +1042 -809
  24. package/dist/{delete-3QH7VYIN.js → delete-UOU4AFQN.js} +7 -3
  25. package/dist/down-AZVH5TCD.js +11 -0
  26. package/dist/{env-4D4REPJF.js → env-7GLUJCWS.js} +2 -2
  27. package/dist/{history-OEONB53Z.js → history-H72ZUIBN.js} +2 -2
  28. package/dist/{import-MXJB2EII.js → import-AVKQJDYC.js} +2 -2
  29. package/dist/{logs-DF342W4M.js → logs-EDGK26AK.js} +1 -1
  30. package/dist/{message-ADHWFHSI.js → message-SCOQDR3P.js} +2 -2
  31. package/dist/{package-VQOE7JNH.js → package-T2WAVJOU.js} +1 -1
  32. package/dist/restart-O4ETYLJF.js +29 -0
  33. package/dist/{schedule-NAG6F463.js → schedule-S6QVC5ON.js} +2 -2
  34. package/dist/send-G7PE4DOJ.js +72 -0
  35. package/dist/{setup-RPRRGG2F.js → setup-F4TCWVSP.js} +2 -2
  36. package/dist/{start-TUOXDSFL.js → start-VHQ7LNWM.js} +2 -2
  37. package/dist/{status-A36EHRO4.js → status-QAJWXKMZ.js} +2 -2
  38. package/dist/{stop-AOJZLQ5X.js → stop-CAGCT5NI.js} +2 -2
  39. package/dist/up-RWZF6MLT.js +12 -0
  40. package/dist/{update-LPSIAWQ2.js → update-F7QWV2LB.js} +2 -2
  41. package/dist/{update-check-Y33QDCFL.js → update-check-B4J6IEQ4.js} +2 -2
  42. package/dist/{upgrade-FX2TKJ2S.js → upgrade-YXKPWDRU.js} +2 -2
  43. package/dist/{variant-LAB67OC2.js → variant-4Z6W3PP6.js} +2 -2
  44. package/dist/web-assets/assets/index-B1CqjUYD.js +308 -0
  45. package/dist/web-assets/index.html +1 -1
  46. package/package.json +1 -1
  47. package/templates/_base/.init/.config/scripts/session-reader.ts +59 -0
  48. package/templates/_base/_skills/sessions/SKILL.md +49 -0
  49. package/templates/_base/_skills/volute-agent/SKILL.md +13 -9
  50. package/templates/_base/src/lib/format-prefix.ts +6 -0
  51. package/templates/_base/src/lib/router.ts +30 -3
  52. package/templates/_base/src/lib/session-monitor.ts +400 -0
  53. package/templates/_base/src/lib/types.ts +2 -0
  54. package/templates/agent-sdk/src/agent.ts +16 -0
  55. package/templates/agent-sdk/src/lib/hooks/session-context.ts +32 -0
  56. package/templates/pi/src/agent.ts +7 -1
  57. package/templates/pi/src/lib/session-context-extension.ts +33 -0
  58. package/dist/channel-MK5OK2SI.js +0 -113
  59. package/dist/chunk-SMISE4SV.js +0 -226
  60. package/dist/conversation-ERXEQZTY.js +0 -163
  61. package/dist/send-66QMKRUH.js +0 -75
  62. package/dist/web-assets/assets/index-BbRmoxoA.js +0 -308
@@ -1,8 +1,13 @@
1
1
  #!/usr/bin/env node
2
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
+
3
8
  // src/connectors/sdk.ts
4
- import { existsSync, readFileSync } from "fs";
5
- import { resolve } from "path";
9
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
10
+ import { join, resolve } from "path";
6
11
  function loadEnv() {
7
12
  const agentPort = process.env.VOLUTE_AGENT_PORT;
8
13
  const agentName = process.env.VOLUTE_AGENT_NAME;
@@ -92,6 +97,15 @@ function onShutdown(cleanup) {
92
97
  process.on("SIGINT", handler);
93
98
  process.on("SIGTERM", handler);
94
99
  }
100
+ function reportTyping(env, channel, sender, active) {
101
+ fetch(`${env.baseUrl}/typing`, {
102
+ method: "POST",
103
+ headers: getHeaders(env),
104
+ body: JSON.stringify({ channel, sender, active })
105
+ }).catch((err) => {
106
+ console.warn(`[typing] failed to report for ${sender} on ${channel}: ${err}`);
107
+ });
108
+ }
95
109
  async function fireAndForget(env, payload) {
96
110
  try {
97
111
  const res = await fetch(`${env.baseUrl}/message`, {
@@ -157,12 +171,63 @@ async function handleAgentMessage(env, payload, handlers) {
157
171
  await handlers.onError(errMsg);
158
172
  }
159
173
  }
174
+ function buildChannelSlug(platform, meta) {
175
+ if (meta.isDM) {
176
+ if (meta.recipients && meta.recipients.length > 0) {
177
+ const sorted = meta.recipients.map(slugify).sort();
178
+ return `${platform}:@${sorted.join(",")}`;
179
+ }
180
+ if (meta.senderName) {
181
+ return `${platform}:@${slugify(meta.senderName)}`;
182
+ }
183
+ }
184
+ if (meta.channelName && meta.serverName) {
185
+ return `${platform}:${slugify(meta.serverName)}/${slugify(meta.channelName)}`;
186
+ }
187
+ if (meta.channelName) {
188
+ return `${platform}:${slugify(meta.channelName)}`;
189
+ }
190
+ if (meta.platformId) {
191
+ return `${platform}:${meta.platformId}`;
192
+ }
193
+ return `${platform}:unknown`;
194
+ }
195
+ function readChannelMap(agentDir) {
196
+ const filePath = join(agentDir, ".volute", "channels.json");
197
+ if (!existsSync(filePath)) return {};
198
+ try {
199
+ return JSON.parse(readFileSync(filePath, "utf-8"));
200
+ } catch {
201
+ return {};
202
+ }
203
+ }
204
+ function writeChannelEntry(agentDir, slug, entry) {
205
+ const voluteDir = join(agentDir, ".volute");
206
+ mkdirSync(voluteDir, { recursive: true });
207
+ const filePath = join(voluteDir, "channels.json");
208
+ const map = readChannelMap(agentDir);
209
+ map[slug] = entry;
210
+ writeFileSync(filePath, JSON.stringify(map, null, 2) + "\n");
211
+ }
212
+ function resolveChannelId(agentDir, slug) {
213
+ const map = readChannelMap(agentDir);
214
+ if (map[slug]) {
215
+ return map[slug].platformId;
216
+ }
217
+ const colonIndex = slug.indexOf(":");
218
+ return colonIndex >= 0 ? slug.slice(colonIndex + 1) : slug;
219
+ }
160
220
 
161
221
  export {
222
+ slugify,
162
223
  loadEnv,
163
224
  loadFollowedChannels,
164
225
  splitMessage,
165
226
  onShutdown,
227
+ reportTyping,
166
228
  fireAndForget,
167
- handleAgentMessage
229
+ handleAgentMessage,
230
+ buildChannelSlug,
231
+ writeChannelEntry,
232
+ resolveChannelId
168
233
  };
@@ -4,21 +4,31 @@ import {
4
4
  } from "./chunk-D424ZQGI.js";
5
5
  import {
6
6
  voluteHome
7
- } from "./chunk-UX25Z2ND.js";
8
- import "./chunk-K3NQKI34.js";
7
+ } from "./chunk-UWHWAPGO.js";
9
8
 
10
9
  // src/commands/up.ts
11
10
  import { spawn } from "child_process";
12
11
  import { existsSync, mkdirSync, openSync, readFileSync } from "fs";
13
12
  import { dirname, resolve } from "path";
13
+ function readGlobalConfig() {
14
+ const configPath = resolve(voluteHome(), "config.json");
15
+ if (!existsSync(configPath)) return {};
16
+ try {
17
+ return JSON.parse(readFileSync(configPath, "utf-8"));
18
+ } catch (err) {
19
+ console.error(`Invalid config file ${configPath}: ${err instanceof Error ? err.message : err}`);
20
+ process.exit(1);
21
+ }
22
+ }
14
23
  async function run(args) {
15
24
  const { flags } = parseArgs(args, {
16
25
  port: { type: "number" },
17
26
  host: { type: "string" },
18
27
  foreground: { type: "boolean" }
19
28
  });
20
- const port = flags.port ?? 4200;
21
- const hostname = flags.host ?? "127.0.0.1";
29
+ const config = readGlobalConfig();
30
+ const port = flags.port ?? config.port ?? 4200;
31
+ const hostname = flags.host ?? config.hostname ?? "127.0.0.1";
22
32
  const home = voluteHome();
23
33
  const pidPath = resolve(home, "daemon.pid");
24
34
  if (existsSync(pidPath)) {
@@ -92,6 +102,8 @@ async function run(args) {
92
102
  console.error(`Check logs: ${logFile}`);
93
103
  process.exit(1);
94
104
  }
105
+
95
106
  export {
107
+ readGlobalConfig,
96
108
  run
97
109
  };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteHome
4
- } from "./chunk-UX25Z2ND.js";
4
+ } from "./chunk-UWHWAPGO.js";
5
5
 
6
6
  // src/lib/env.ts
7
7
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteHome
4
- } from "./chunk-UX25Z2ND.js";
4
+ } from "./chunk-UWHWAPGO.js";
5
5
 
6
6
  // src/lib/daemon-client.ts
7
7
  import { existsSync, readFileSync } from "fs";
@@ -1,13 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteHome
4
- } from "./chunk-UX25Z2ND.js";
5
- import "./chunk-K3NQKI34.js";
4
+ } from "./chunk-UWHWAPGO.js";
6
5
 
7
6
  // src/commands/down.ts
8
7
  import { existsSync, readFileSync, unlinkSync } from "fs";
9
8
  import { resolve } from "path";
10
- async function run(_args) {
9
+ async function stopDaemon() {
11
10
  const home = voluteHome();
12
11
  const pidPath = resolve(home, "daemon.pid");
13
12
  if (!existsSync(pidPath)) {
@@ -28,16 +27,21 @@ async function run(_args) {
28
27
  url.port = String(port);
29
28
  const res = await fetch(`${url.origin}/api/health`);
30
29
  if (res.ok) {
31
- console.error(`Daemon appears to be running on port ${port} but PID file is missing.`);
32
- console.error(`Kill the process manually: lsof -ti :${port} | xargs kill`);
33
- process.exit(1);
30
+ return { stopped: false, reason: "orphan", port };
34
31
  }
35
32
  } catch {
36
33
  }
37
- console.error("Daemon is not running (no PID file found).");
38
- process.exit(1);
34
+ return { stopped: false, reason: "not-running" };
39
35
  }
40
36
  const pid = parseInt(readFileSync(pidPath, "utf-8").trim(), 10);
37
+ if (!Number.isInteger(pid) || pid <= 0) {
38
+ console.error(`Stale or corrupt PID file (${pidPath}), removing.`);
39
+ try {
40
+ unlinkSync(pidPath);
41
+ } catch {
42
+ }
43
+ return { stopped: false, reason: "not-running" };
44
+ }
41
45
  try {
42
46
  process.kill(pid, 0);
43
47
  } catch {
@@ -46,21 +50,28 @@ async function run(_args) {
46
50
  } catch {
47
51
  }
48
52
  console.log("Daemon was not running (cleaned up stale PID file).");
49
- return;
53
+ return { stopped: false, reason: "not-running" };
50
54
  }
51
55
  try {
52
56
  process.kill(-pid, "SIGTERM");
53
57
  console.log(`Sent SIGTERM to daemon group (pid ${pid})`);
54
58
  } catch {
55
- process.kill(pid, "SIGTERM");
56
- console.log(`Sent SIGTERM to daemon (pid ${pid})`);
59
+ try {
60
+ process.kill(pid, "SIGTERM");
61
+ console.log(`Sent SIGTERM to daemon (pid ${pid})`);
62
+ } catch (e) {
63
+ console.error(
64
+ `Failed to send SIGTERM to daemon (pid ${pid}): ${e instanceof Error ? e.message : e}`
65
+ );
66
+ return { stopped: false, reason: "kill-failed", port: pid };
67
+ }
57
68
  }
58
69
  const maxWait = 1e4;
59
70
  const start = Date.now();
60
71
  while (Date.now() - start < maxWait) {
61
72
  if (!existsSync(pidPath)) {
62
73
  console.log("Daemon stopped.");
63
- return;
74
+ return { stopped: true, clean: true };
64
75
  }
65
76
  await new Promise((r) => setTimeout(r, 200));
66
77
  }
@@ -69,11 +80,35 @@ async function run(_args) {
69
80
  } catch {
70
81
  try {
71
82
  process.kill(pid, "SIGKILL");
72
- } catch {
83
+ } catch (e) {
84
+ console.error(
85
+ `Failed to force-kill daemon (pid ${pid}): ${e instanceof Error ? e.message : e}`
86
+ );
87
+ console.error(`The daemon may still be running. Kill it manually: kill -9 ${pid}`);
88
+ return { stopped: false, reason: "kill-failed" };
73
89
  }
74
90
  }
91
+ try {
92
+ unlinkSync(pidPath);
93
+ } catch {
94
+ }
95
+ await new Promise((r) => setTimeout(r, 500));
75
96
  console.error("Daemon did not exit cleanly, sent SIGKILL.");
97
+ return { stopped: true, clean: false };
76
98
  }
99
+ async function run(_args) {
100
+ const result = await stopDaemon();
101
+ if (result.stopped) return;
102
+ if (result.reason === "orphan") {
103
+ console.error(`Daemon appears to be running on port ${result.port} but PID file is missing.`);
104
+ console.error(`Kill the process manually: lsof -ti :${result.port} | xargs kill`);
105
+ } else if (result.reason === "not-running") {
106
+ console.error("Daemon is not running (no PID file found).");
107
+ }
108
+ process.exit(1);
109
+ }
110
+
77
111
  export {
112
+ stopDaemon,
78
113
  run
79
114
  };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  voluteHome
4
- } from "./chunk-UX25Z2ND.js";
4
+ } from "./chunk-UWHWAPGO.js";
5
5
 
6
6
  // src/lib/update-check.ts
7
7
  import { existsSync, readFileSync, writeFileSync } from "fs";
@@ -28,6 +28,7 @@ function writeCache(latest) {
28
28
  function getCurrentVersion() {
29
29
  const thisDir = new URL(".", import.meta.url).pathname;
30
30
  const candidates = [
31
+ resolve(thisDir, "../package.json"),
31
32
  resolve(thisDir, "../../package.json"),
32
33
  resolve(thisDir, "../../../package.json")
33
34
  ];
@@ -90,6 +90,12 @@ function nextPort() {
90
90
  if (port > 65535) throw new Error("No available ports \u2014 all ports 4100-65535 are allocated");
91
91
  return port;
92
92
  }
93
+ function daemonLoopback() {
94
+ const host = process.env.VOLUTE_DAEMON_HOSTNAME || "127.0.0.1";
95
+ if (host === "0.0.0.0") return "127.0.0.1";
96
+ if (host === "::") return "[::1]";
97
+ return host;
98
+ }
93
99
  function resolveAgent(name) {
94
100
  const [baseName, variantName] = name.split("@", 2);
95
101
  const entry = findAgent(baseName);
@@ -227,5 +233,6 @@ export {
227
233
  findAgent,
228
234
  agentDir,
229
235
  nextPort,
236
+ daemonLoopback,
230
237
  resolveAgent
231
238
  };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  validateAgentName
4
- } from "./chunk-UX25Z2ND.js";
4
+ } from "./chunk-UWHWAPGO.js";
5
5
 
6
6
  // src/lib/isolation.ts
7
7
  import { execFile, execFileSync } from "child_process";