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.
- package/dist/{agent-Z2B6EFEQ.js → agent-7JF7MT73.js} +13 -9
- package/dist/{agent-manager-PXBKA2GK.js → agent-manager-IMZ7ZMBF.js} +4 -4
- package/dist/channel-SMCNOIVQ.js +262 -0
- package/dist/{chunk-MW2KFO3B.js → chunk-62X577Y7.js} +10 -8
- package/dist/chunk-7ACDT3P2.js +265 -0
- package/dist/{chunk-MXUCNIBG.js → chunk-BX7KI4S3.js} +68 -3
- package/dist/{up-7ILD7GU7.js → chunk-EG45HBSJ.js} +16 -4
- package/dist/{chunk-HE67X4T6.js → chunk-H7AMDUIA.js} +1 -1
- package/dist/{chunk-7L4AN5D4.js → chunk-JR4UXCTO.js} +1 -1
- package/dist/{down-O7IFZLVJ.js → chunk-LLJNZPCU.js} +48 -13
- package/dist/{chunk-5X7HGB6L.js → chunk-NKXULRSW.js} +2 -1
- package/dist/{chunk-UX25Z2ND.js → chunk-UWHWAPGO.js} +7 -0
- package/dist/{chunk-UAVD2AHX.js → chunk-W76KWE23.js} +1 -1
- package/dist/chunk-ZZOOTYXK.js +583 -0
- package/dist/cli.js +22 -21
- package/dist/{connector-LYEMXQEV.js → connector-Y7JPNROO.js} +3 -3
- package/dist/connectors/discord.js +38 -7
- package/dist/connectors/slack.js +22 -3
- package/dist/connectors/telegram.js +34 -4
- package/dist/{create-RVCZN6HE.js → create-G525LWEA.js} +2 -2
- package/dist/{daemon-client-ZY6UUN2M.js → daemon-client-442IV43D.js} +2 -2
- package/dist/daemon-restart-4HVEKYFY.js +23 -0
- package/dist/daemon.js +1042 -809
- package/dist/{delete-3QH7VYIN.js → delete-UOU4AFQN.js} +7 -3
- package/dist/down-AZVH5TCD.js +11 -0
- package/dist/{env-4D4REPJF.js → env-7GLUJCWS.js} +2 -2
- package/dist/{history-OEONB53Z.js → history-H72ZUIBN.js} +2 -2
- package/dist/{import-MXJB2EII.js → import-AVKQJDYC.js} +2 -2
- package/dist/{logs-DF342W4M.js → logs-EDGK26AK.js} +1 -1
- package/dist/{message-ADHWFHSI.js → message-SCOQDR3P.js} +2 -2
- package/dist/{package-VQOE7JNH.js → package-T2WAVJOU.js} +1 -1
- package/dist/restart-O4ETYLJF.js +29 -0
- package/dist/{schedule-NAG6F463.js → schedule-S6QVC5ON.js} +2 -2
- package/dist/send-G7PE4DOJ.js +72 -0
- package/dist/{setup-RPRRGG2F.js → setup-F4TCWVSP.js} +2 -2
- package/dist/{start-TUOXDSFL.js → start-VHQ7LNWM.js} +2 -2
- package/dist/{status-A36EHRO4.js → status-QAJWXKMZ.js} +2 -2
- package/dist/{stop-AOJZLQ5X.js → stop-CAGCT5NI.js} +2 -2
- package/dist/up-RWZF6MLT.js +12 -0
- package/dist/{update-LPSIAWQ2.js → update-F7QWV2LB.js} +2 -2
- package/dist/{update-check-Y33QDCFL.js → update-check-B4J6IEQ4.js} +2 -2
- package/dist/{upgrade-FX2TKJ2S.js → upgrade-YXKPWDRU.js} +2 -2
- package/dist/{variant-LAB67OC2.js → variant-4Z6W3PP6.js} +2 -2
- package/dist/web-assets/assets/index-B1CqjUYD.js +308 -0
- package/dist/web-assets/index.html +1 -1
- package/package.json +1 -1
- package/templates/_base/.init/.config/scripts/session-reader.ts +59 -0
- package/templates/_base/_skills/sessions/SKILL.md +49 -0
- package/templates/_base/_skills/volute-agent/SKILL.md +13 -9
- package/templates/_base/src/lib/format-prefix.ts +6 -0
- package/templates/_base/src/lib/router.ts +30 -3
- package/templates/_base/src/lib/session-monitor.ts +400 -0
- package/templates/_base/src/lib/types.ts +2 -0
- package/templates/agent-sdk/src/agent.ts +16 -0
- package/templates/agent-sdk/src/lib/hooks/session-context.ts +32 -0
- package/templates/pi/src/agent.ts +7 -1
- package/templates/pi/src/lib/session-context-extension.ts +33 -0
- package/dist/channel-MK5OK2SI.js +0 -113
- package/dist/chunk-SMISE4SV.js +0 -226
- package/dist/conversation-ERXEQZTY.js +0 -163
- package/dist/send-66QMKRUH.js +0 -75
- package/dist/web-assets/assets/index-BbRmoxoA.js +0 -308
|
@@ -5,6 +5,7 @@ import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
|
5
5
|
import { createAutoCommitHook } from "./lib/hooks/auto-commit.js";
|
|
6
6
|
import { createIdentityReloadHook } from "./lib/hooks/identity-reload.js";
|
|
7
7
|
import { createPreCompactHook } from "./lib/hooks/pre-compact.js";
|
|
8
|
+
import { createSessionContextHook } from "./lib/hooks/session-context.js";
|
|
8
9
|
import { log, logText, logThinking, logToolUse } from "./lib/logger.js";
|
|
9
10
|
import { createMessageChannel } from "./lib/message-channel.js";
|
|
10
11
|
import type {
|
|
@@ -136,6 +137,12 @@ export function createAgent(options: {
|
|
|
136
137
|
});
|
|
137
138
|
});
|
|
138
139
|
|
|
140
|
+
const sessionContext = createSessionContextHook({
|
|
141
|
+
currentSession: session.name,
|
|
142
|
+
sessionsDir: options.sessionsDir,
|
|
143
|
+
cwd: options.cwd,
|
|
144
|
+
});
|
|
145
|
+
|
|
139
146
|
return query({
|
|
140
147
|
prompt: session.channel.iterable,
|
|
141
148
|
options: {
|
|
@@ -150,6 +157,7 @@ export function createAgent(options: {
|
|
|
150
157
|
hooks: {
|
|
151
158
|
PostToolUse: postToolUseHooks,
|
|
152
159
|
PreCompact: [{ hooks: [preCompact.hook] }],
|
|
160
|
+
UserPromptSubmit: [{ hooks: [sessionContext.hook] }],
|
|
153
161
|
},
|
|
154
162
|
},
|
|
155
163
|
});
|
|
@@ -182,6 +190,14 @@ export function createAgent(options: {
|
|
|
182
190
|
}
|
|
183
191
|
if (msg.type === "result") {
|
|
184
192
|
log("agent", `session "${session.name}": turn done`);
|
|
193
|
+
const result = msg as { usage?: { input_tokens?: number; output_tokens?: number } };
|
|
194
|
+
if (result.usage) {
|
|
195
|
+
broadcastToSession(session, {
|
|
196
|
+
type: "usage",
|
|
197
|
+
input_tokens: result.usage.input_tokens ?? 0,
|
|
198
|
+
output_tokens: result.usage.output_tokens ?? 0,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
185
201
|
broadcastToSession(session, { type: "done" });
|
|
186
202
|
session.currentMessageId = undefined;
|
|
187
203
|
if (identityReload.needsReload()) {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import type { HookCallback } from "@anthropic-ai/claude-agent-sdk";
|
|
3
|
+
import { getSessionUpdates, resolveAgentSdkJsonl } from "../session-monitor.js";
|
|
4
|
+
|
|
5
|
+
export function createSessionContextHook(options: {
|
|
6
|
+
currentSession: string;
|
|
7
|
+
sessionsDir: string;
|
|
8
|
+
cwd: string;
|
|
9
|
+
}) {
|
|
10
|
+
const hook: HookCallback = async () => {
|
|
11
|
+
try {
|
|
12
|
+
const summary = getSessionUpdates({
|
|
13
|
+
currentSession: options.currentSession,
|
|
14
|
+
sessionsDir: options.sessionsDir,
|
|
15
|
+
cursorFile: resolve(options.sessionsDir, "../session-cursors.json"),
|
|
16
|
+
jsonlResolver: (name) => resolveAgentSdkJsonl(options.sessionsDir, name, options.cwd),
|
|
17
|
+
format: "agent-sdk",
|
|
18
|
+
});
|
|
19
|
+
if (!summary) return {};
|
|
20
|
+
return {
|
|
21
|
+
hookSpecificOutput: {
|
|
22
|
+
hookEventName: "UserPromptSubmit" as const,
|
|
23
|
+
additionalContext: summary,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
} catch {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return { hook };
|
|
32
|
+
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
} from "@mariozechner/pi-coding-agent";
|
|
12
12
|
import { commitFileChange } from "./lib/auto-commit.js";
|
|
13
13
|
import { log, logText, logThinking, logToolResult, logToolUse } from "./lib/logger.js";
|
|
14
|
+
import { createSessionContextExtension } from "./lib/session-context-extension.js";
|
|
14
15
|
import type {
|
|
15
16
|
HandlerMeta,
|
|
16
17
|
HandlerResolver,
|
|
@@ -137,11 +138,16 @@ export function createAgent(options: {
|
|
|
137
138
|
retry: { enabled: true, maxRetries: 3 },
|
|
138
139
|
});
|
|
139
140
|
|
|
141
|
+
const sessionContextExtension = createSessionContextExtension({
|
|
142
|
+
currentSession: session.name,
|
|
143
|
+
cwd: options.cwd,
|
|
144
|
+
});
|
|
145
|
+
|
|
140
146
|
const resourceLoader = new DefaultResourceLoader({
|
|
141
147
|
cwd: options.cwd,
|
|
142
148
|
settingsManager,
|
|
143
149
|
systemPrompt: options.systemPrompt,
|
|
144
|
-
extensionFactories: [preCompactExtension],
|
|
150
|
+
extensionFactories: [preCompactExtension, sessionContextExtension],
|
|
145
151
|
});
|
|
146
152
|
await resourceLoader.reload();
|
|
147
153
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import type { ExtensionFactory } from "@mariozechner/pi-coding-agent";
|
|
3
|
+
import { getSessionUpdates, resolvePiJsonl } from "./session-monitor.js";
|
|
4
|
+
|
|
5
|
+
export function createSessionContextExtension(options: {
|
|
6
|
+
currentSession: string;
|
|
7
|
+
cwd: string;
|
|
8
|
+
}): ExtensionFactory {
|
|
9
|
+
return (pi) => {
|
|
10
|
+
pi.on("before_agent_start", () => {
|
|
11
|
+
try {
|
|
12
|
+
const sessionsDir = resolve(options.cwd, ".volute/pi-sessions");
|
|
13
|
+
const summary = getSessionUpdates({
|
|
14
|
+
currentSession: options.currentSession,
|
|
15
|
+
sessionsDir,
|
|
16
|
+
cursorFile: resolve(options.cwd, ".volute/session-cursors.json"),
|
|
17
|
+
jsonlResolver: (name) => resolvePiJsonl(sessionsDir, name),
|
|
18
|
+
format: "pi",
|
|
19
|
+
});
|
|
20
|
+
if (!summary) return {};
|
|
21
|
+
return {
|
|
22
|
+
message: {
|
|
23
|
+
customType: "session-update",
|
|
24
|
+
content: summary,
|
|
25
|
+
display: true,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
} catch {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
}
|
package/dist/channel-MK5OK2SI.js
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
readStdin
|
|
4
|
-
} from "./chunk-ZYGKG6VC.js";
|
|
5
|
-
import {
|
|
6
|
-
resolveAgentName
|
|
7
|
-
} from "./chunk-AZEL2IEK.js";
|
|
8
|
-
import {
|
|
9
|
-
getChannelDriver
|
|
10
|
-
} from "./chunk-SMISE4SV.js";
|
|
11
|
-
import {
|
|
12
|
-
loadMergedEnv
|
|
13
|
-
} from "./chunk-HE67X4T6.js";
|
|
14
|
-
import {
|
|
15
|
-
parseArgs
|
|
16
|
-
} from "./chunk-D424ZQGI.js";
|
|
17
|
-
import {
|
|
18
|
-
resolveAgent
|
|
19
|
-
} from "./chunk-UX25Z2ND.js";
|
|
20
|
-
import "./chunk-K3NQKI34.js";
|
|
21
|
-
|
|
22
|
-
// src/commands/channel.ts
|
|
23
|
-
async function run(args) {
|
|
24
|
-
const subcommand = args[0];
|
|
25
|
-
switch (subcommand) {
|
|
26
|
-
case "read":
|
|
27
|
-
await readChannel(args.slice(1));
|
|
28
|
-
break;
|
|
29
|
-
case "send":
|
|
30
|
-
await sendChannel(args.slice(1));
|
|
31
|
-
break;
|
|
32
|
-
case "--help":
|
|
33
|
-
case "-h":
|
|
34
|
-
case void 0:
|
|
35
|
-
printUsage();
|
|
36
|
-
break;
|
|
37
|
-
default:
|
|
38
|
-
printUsage();
|
|
39
|
-
process.exit(1);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
function printUsage() {
|
|
43
|
-
console.log(`Usage:
|
|
44
|
-
volute channel read <channel-uri> [--limit N] [--agent <name>]
|
|
45
|
-
volute channel send <channel-uri> "<message>" [--agent <name>]
|
|
46
|
-
echo "message" | volute channel send <channel-uri> [--agent <name>]`);
|
|
47
|
-
}
|
|
48
|
-
async function readChannel(args) {
|
|
49
|
-
const { positional, flags } = parseArgs(args, {
|
|
50
|
-
agent: { type: "string" },
|
|
51
|
-
limit: { type: "number" }
|
|
52
|
-
});
|
|
53
|
-
const uri = positional[0];
|
|
54
|
-
if (!uri) {
|
|
55
|
-
console.error("Usage: volute channel read <channel-uri> [--limit N] [--agent <name>]");
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
const agentName = resolveAgentName(flags);
|
|
59
|
-
const { platform, channelId } = parseUri(uri);
|
|
60
|
-
const driver = requireDriver(platform);
|
|
61
|
-
const { dir } = resolveAgent(agentName);
|
|
62
|
-
const env = { ...loadMergedEnv(dir), VOLUTE_AGENT: agentName };
|
|
63
|
-
try {
|
|
64
|
-
const limit = flags.limit ?? 20;
|
|
65
|
-
const output = await driver.read(env, channelId, limit);
|
|
66
|
-
console.log(output);
|
|
67
|
-
} catch (err) {
|
|
68
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
69
|
-
process.exit(1);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
async function sendChannel(args) {
|
|
73
|
-
const { positional, flags } = parseArgs(args, {
|
|
74
|
-
agent: { type: "string" }
|
|
75
|
-
});
|
|
76
|
-
const uri = positional[0];
|
|
77
|
-
const message = positional[1] ?? await readStdin();
|
|
78
|
-
if (!uri || !message) {
|
|
79
|
-
console.error('Usage: volute channel send <channel-uri> "<message>" [--agent <name>]');
|
|
80
|
-
console.error(' echo "message" | volute channel send <channel-uri> [--agent <name>]');
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
const agentName = resolveAgentName(flags);
|
|
84
|
-
const { platform, channelId } = parseUri(uri);
|
|
85
|
-
const driver = requireDriver(platform);
|
|
86
|
-
const { dir } = resolveAgent(agentName);
|
|
87
|
-
const env = { ...loadMergedEnv(dir), VOLUTE_AGENT: agentName };
|
|
88
|
-
try {
|
|
89
|
-
await driver.send(env, channelId, message);
|
|
90
|
-
} catch (err) {
|
|
91
|
-
console.error(err instanceof Error ? err.message : String(err));
|
|
92
|
-
process.exit(1);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function parseUri(uri) {
|
|
96
|
-
const colonIdx = uri.indexOf(":");
|
|
97
|
-
if (colonIdx === -1) {
|
|
98
|
-
console.error(`Invalid channel URI: ${uri} (expected format: platform:id)`);
|
|
99
|
-
process.exit(1);
|
|
100
|
-
}
|
|
101
|
-
return { platform: uri.slice(0, colonIdx), channelId: uri.slice(colonIdx + 1) };
|
|
102
|
-
}
|
|
103
|
-
function requireDriver(platform) {
|
|
104
|
-
const driver = getChannelDriver(platform);
|
|
105
|
-
if (!driver) {
|
|
106
|
-
console.error(`No channel driver for platform: ${platform}`);
|
|
107
|
-
process.exit(1);
|
|
108
|
-
}
|
|
109
|
-
return driver;
|
|
110
|
-
}
|
|
111
|
-
export {
|
|
112
|
-
run
|
|
113
|
-
};
|
package/dist/chunk-SMISE4SV.js
DELETED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
voluteHome
|
|
4
|
-
} from "./chunk-UX25Z2ND.js";
|
|
5
|
-
import {
|
|
6
|
-
__export
|
|
7
|
-
} from "./chunk-K3NQKI34.js";
|
|
8
|
-
|
|
9
|
-
// src/lib/channels/discord.ts
|
|
10
|
-
var discord_exports = {};
|
|
11
|
-
__export(discord_exports, {
|
|
12
|
-
read: () => read,
|
|
13
|
-
send: () => send
|
|
14
|
-
});
|
|
15
|
-
var API_BASE = "https://discord.com/api/v10";
|
|
16
|
-
function requireToken(env) {
|
|
17
|
-
const token = env.DISCORD_TOKEN;
|
|
18
|
-
if (!token) throw new Error("DISCORD_TOKEN not set");
|
|
19
|
-
return token;
|
|
20
|
-
}
|
|
21
|
-
async function read(env, channelId, limit) {
|
|
22
|
-
const token = requireToken(env);
|
|
23
|
-
const res = await fetch(`${API_BASE}/channels/${channelId}/messages?limit=${limit}`, {
|
|
24
|
-
headers: { Authorization: `Bot ${token}` }
|
|
25
|
-
});
|
|
26
|
-
if (!res.ok) {
|
|
27
|
-
throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
|
|
28
|
-
}
|
|
29
|
-
const messages = await res.json();
|
|
30
|
-
return messages.reverse().map((m) => `${m.author.username}: ${m.content}`).join("\n");
|
|
31
|
-
}
|
|
32
|
-
async function send(env, channelId, message) {
|
|
33
|
-
const token = requireToken(env);
|
|
34
|
-
const res = await fetch(`${API_BASE}/channels/${channelId}/messages`, {
|
|
35
|
-
method: "POST",
|
|
36
|
-
headers: {
|
|
37
|
-
Authorization: `Bot ${token}`,
|
|
38
|
-
"Content-Type": "application/json"
|
|
39
|
-
},
|
|
40
|
-
body: JSON.stringify({ content: message })
|
|
41
|
-
});
|
|
42
|
-
if (!res.ok) {
|
|
43
|
-
throw new Error(`Discord API error: ${res.status} ${res.statusText}`);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// src/lib/channels/slack.ts
|
|
48
|
-
var slack_exports = {};
|
|
49
|
-
__export(slack_exports, {
|
|
50
|
-
read: () => read2,
|
|
51
|
-
send: () => send2
|
|
52
|
-
});
|
|
53
|
-
var API_BASE2 = "https://slack.com/api";
|
|
54
|
-
function requireToken2(env) {
|
|
55
|
-
const token = env.SLACK_BOT_TOKEN;
|
|
56
|
-
if (!token) throw new Error("SLACK_BOT_TOKEN not set");
|
|
57
|
-
return token;
|
|
58
|
-
}
|
|
59
|
-
async function slackApi(token, method, body) {
|
|
60
|
-
const res = await fetch(`${API_BASE2}/${method}`, {
|
|
61
|
-
method: "POST",
|
|
62
|
-
headers: {
|
|
63
|
-
Authorization: `Bearer ${token}`,
|
|
64
|
-
"Content-Type": "application/json"
|
|
65
|
-
},
|
|
66
|
-
body: JSON.stringify(body)
|
|
67
|
-
});
|
|
68
|
-
if (!res.ok) {
|
|
69
|
-
throw new Error(`Slack API HTTP error: ${res.status} ${res.statusText}`);
|
|
70
|
-
}
|
|
71
|
-
const data = await res.json();
|
|
72
|
-
if (!data.ok) {
|
|
73
|
-
throw new Error(`Slack API error: ${data.error}`);
|
|
74
|
-
}
|
|
75
|
-
return data;
|
|
76
|
-
}
|
|
77
|
-
async function read2(env, channelId, limit) {
|
|
78
|
-
const token = requireToken2(env);
|
|
79
|
-
const data = await slackApi(token, "conversations.history", {
|
|
80
|
-
channel: channelId,
|
|
81
|
-
limit
|
|
82
|
-
});
|
|
83
|
-
return data.messages.reverse().map((m) => `${m.user ?? m.bot_id ?? "unknown"}: ${m.text}`).join("\n");
|
|
84
|
-
}
|
|
85
|
-
async function send2(env, channelId, message) {
|
|
86
|
-
const token = requireToken2(env);
|
|
87
|
-
await slackApi(token, "chat.postMessage", {
|
|
88
|
-
channel: channelId,
|
|
89
|
-
text: message
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// src/lib/channels/telegram.ts
|
|
94
|
-
var telegram_exports = {};
|
|
95
|
-
__export(telegram_exports, {
|
|
96
|
-
read: () => read3,
|
|
97
|
-
send: () => send3
|
|
98
|
-
});
|
|
99
|
-
var API_BASE3 = "https://api.telegram.org";
|
|
100
|
-
function requireToken3(env) {
|
|
101
|
-
const token = env.TELEGRAM_BOT_TOKEN;
|
|
102
|
-
if (!token) throw new Error("TELEGRAM_BOT_TOKEN not set");
|
|
103
|
-
return token;
|
|
104
|
-
}
|
|
105
|
-
async function read3(_env, _channelId, _limit) {
|
|
106
|
-
throw new Error(
|
|
107
|
-
"Telegram Bot API does not support reading chat history. Use volute channel send instead."
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
async function send3(env, chatId, message) {
|
|
111
|
-
const token = requireToken3(env);
|
|
112
|
-
const res = await fetch(`${API_BASE3}/bot${token}/sendMessage`, {
|
|
113
|
-
method: "POST",
|
|
114
|
-
headers: { "Content-Type": "application/json" },
|
|
115
|
-
body: JSON.stringify({ chat_id: chatId, text: message })
|
|
116
|
-
});
|
|
117
|
-
if (!res.ok) {
|
|
118
|
-
const body = await res.text().catch(() => "");
|
|
119
|
-
throw new Error(`Telegram API error: ${res.status} ${body}`);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// src/lib/channels/volute.ts
|
|
124
|
-
var volute_exports = {};
|
|
125
|
-
__export(volute_exports, {
|
|
126
|
-
read: () => read4,
|
|
127
|
-
send: () => send4
|
|
128
|
-
});
|
|
129
|
-
import { existsSync, readFileSync } from "fs";
|
|
130
|
-
import { resolve } from "path";
|
|
131
|
-
function getDaemonConfig() {
|
|
132
|
-
const configPath = resolve(voluteHome(), "daemon.json");
|
|
133
|
-
if (!existsSync(configPath)) {
|
|
134
|
-
throw new Error("Volute daemon is not running");
|
|
135
|
-
}
|
|
136
|
-
let config;
|
|
137
|
-
try {
|
|
138
|
-
config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
139
|
-
} catch (err) {
|
|
140
|
-
throw new Error(`Failed to parse ${configPath}: ${err}`);
|
|
141
|
-
}
|
|
142
|
-
if (typeof config.port !== "number") {
|
|
143
|
-
throw new Error(`Invalid or missing port in ${configPath}`);
|
|
144
|
-
}
|
|
145
|
-
const url = new URL("http://localhost");
|
|
146
|
-
url.hostname = config.hostname || "localhost";
|
|
147
|
-
url.port = String(config.port);
|
|
148
|
-
return { url: url.origin, token: config.token };
|
|
149
|
-
}
|
|
150
|
-
async function read4(env, conversationId, limit) {
|
|
151
|
-
const agentName = env.VOLUTE_AGENT;
|
|
152
|
-
if (!agentName) throw new Error("VOLUTE_AGENT not set");
|
|
153
|
-
const { url, token } = getDaemonConfig();
|
|
154
|
-
const headers = { Origin: url };
|
|
155
|
-
if (token) headers.Authorization = `Bearer ${token}`;
|
|
156
|
-
const res = await fetch(
|
|
157
|
-
`${url}/api/agents/${encodeURIComponent(agentName)}/conversations/${encodeURIComponent(conversationId)}/messages`,
|
|
158
|
-
{ headers }
|
|
159
|
-
);
|
|
160
|
-
if (!res.ok) {
|
|
161
|
-
throw new Error(`Failed to read conversation: ${res.status} ${res.statusText}`);
|
|
162
|
-
}
|
|
163
|
-
const messages = await res.json();
|
|
164
|
-
return messages.slice(-limit).map((m) => {
|
|
165
|
-
const text = Array.isArray(m.content) ? m.content.filter((b) => b.type === "text").map((b) => b.text).join("") : m.content;
|
|
166
|
-
return `${m.sender_name ?? m.role}: ${text}`;
|
|
167
|
-
}).join("\n");
|
|
168
|
-
}
|
|
169
|
-
async function send4(env, conversationId, message) {
|
|
170
|
-
const agentName = env.VOLUTE_AGENT;
|
|
171
|
-
if (!agentName) throw new Error("VOLUTE_AGENT not set");
|
|
172
|
-
const { url, token } = getDaemonConfig();
|
|
173
|
-
const headers = {
|
|
174
|
-
"Content-Type": "application/json",
|
|
175
|
-
Origin: url
|
|
176
|
-
};
|
|
177
|
-
if (token) headers.Authorization = `Bearer ${token}`;
|
|
178
|
-
const res = await fetch(`${url}/api/agents/${encodeURIComponent(agentName)}/chat`, {
|
|
179
|
-
method: "POST",
|
|
180
|
-
headers,
|
|
181
|
-
body: JSON.stringify({ message, conversationId, sender: agentName })
|
|
182
|
-
});
|
|
183
|
-
if (!res.ok) {
|
|
184
|
-
const data = await res.json().catch(() => ({}));
|
|
185
|
-
throw new Error(data.error ?? `Failed to send: ${res.status}`);
|
|
186
|
-
}
|
|
187
|
-
if (res.body) {
|
|
188
|
-
const reader = res.body.getReader();
|
|
189
|
-
while (true) {
|
|
190
|
-
const { done } = await reader.read();
|
|
191
|
-
if (done) break;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// src/lib/channels.ts
|
|
197
|
-
var CHANNELS = {
|
|
198
|
-
volute: { name: "volute", displayName: "Volute", showToolCalls: true, driver: volute_exports },
|
|
199
|
-
discord: {
|
|
200
|
-
name: "discord",
|
|
201
|
-
displayName: "Discord",
|
|
202
|
-
showToolCalls: false,
|
|
203
|
-
driver: discord_exports
|
|
204
|
-
},
|
|
205
|
-
slack: {
|
|
206
|
-
name: "slack",
|
|
207
|
-
displayName: "Slack",
|
|
208
|
-
showToolCalls: false,
|
|
209
|
-
driver: slack_exports
|
|
210
|
-
},
|
|
211
|
-
telegram: {
|
|
212
|
-
name: "telegram",
|
|
213
|
-
displayName: "Telegram",
|
|
214
|
-
showToolCalls: false,
|
|
215
|
-
driver: telegram_exports
|
|
216
|
-
},
|
|
217
|
-
system: { name: "system", displayName: "System", showToolCalls: false }
|
|
218
|
-
};
|
|
219
|
-
function getChannelDriver(platform) {
|
|
220
|
-
return CHANNELS[platform]?.driver ?? null;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export {
|
|
224
|
-
CHANNELS,
|
|
225
|
-
getChannelDriver
|
|
226
|
-
};
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
readStdin
|
|
4
|
-
} from "./chunk-ZYGKG6VC.js";
|
|
5
|
-
import {
|
|
6
|
-
resolveAgentName
|
|
7
|
-
} from "./chunk-AZEL2IEK.js";
|
|
8
|
-
import {
|
|
9
|
-
summarizeTool
|
|
10
|
-
} from "./chunk-B3R6L2GW.js";
|
|
11
|
-
import {
|
|
12
|
-
parseArgs
|
|
13
|
-
} from "./chunk-D424ZQGI.js";
|
|
14
|
-
import {
|
|
15
|
-
daemonFetch
|
|
16
|
-
} from "./chunk-7L4AN5D4.js";
|
|
17
|
-
import "./chunk-UX25Z2ND.js";
|
|
18
|
-
import "./chunk-K3NQKI34.js";
|
|
19
|
-
|
|
20
|
-
// src/commands/conversation.ts
|
|
21
|
-
import { userInfo } from "os";
|
|
22
|
-
async function run(args) {
|
|
23
|
-
const subcommand = args[0];
|
|
24
|
-
switch (subcommand) {
|
|
25
|
-
case "create":
|
|
26
|
-
await createConversation(args.slice(1));
|
|
27
|
-
break;
|
|
28
|
-
case "list":
|
|
29
|
-
await listConversations(args.slice(1));
|
|
30
|
-
break;
|
|
31
|
-
case "send":
|
|
32
|
-
await sendToConversation(args.slice(1));
|
|
33
|
-
break;
|
|
34
|
-
case "--help":
|
|
35
|
-
case "-h":
|
|
36
|
-
case void 0:
|
|
37
|
-
printUsage();
|
|
38
|
-
break;
|
|
39
|
-
default:
|
|
40
|
-
printUsage();
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function printUsage() {
|
|
45
|
-
console.log(`Usage:
|
|
46
|
-
volute conversation create --participants user1,agent1 [--title "..."] [--agent <name>]
|
|
47
|
-
volute conversation list [--agent <name>]
|
|
48
|
-
volute conversation send <id> "<message>" [--agent <name>]
|
|
49
|
-
echo "message" | volute conversation send <id> [--agent <name>]`);
|
|
50
|
-
}
|
|
51
|
-
async function createConversation(args) {
|
|
52
|
-
const { flags } = parseArgs(args, {
|
|
53
|
-
agent: { type: "string" },
|
|
54
|
-
participants: { type: "string" },
|
|
55
|
-
title: { type: "string" }
|
|
56
|
-
});
|
|
57
|
-
const agentName = resolveAgentName(flags);
|
|
58
|
-
if (!flags.participants) {
|
|
59
|
-
console.error("--participants is required (comma-separated usernames)");
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
const participantNames = flags.participants.split(",").map((s) => s.trim());
|
|
63
|
-
const res = await daemonFetch(`/api/agents/${encodeURIComponent(agentName)}/conversations`, {
|
|
64
|
-
method: "POST",
|
|
65
|
-
headers: { "Content-Type": "application/json" },
|
|
66
|
-
body: JSON.stringify({ participantNames, title: flags.title })
|
|
67
|
-
});
|
|
68
|
-
if (!res.ok) {
|
|
69
|
-
const data = await res.json();
|
|
70
|
-
console.error(data.error ?? `Failed to create conversation: ${res.status}`);
|
|
71
|
-
process.exit(1);
|
|
72
|
-
}
|
|
73
|
-
const conv = await res.json();
|
|
74
|
-
console.log(`Created conversation: ${conv.id}`);
|
|
75
|
-
if (conv.title) console.log(`Title: ${conv.title}`);
|
|
76
|
-
}
|
|
77
|
-
async function listConversations(args) {
|
|
78
|
-
const { flags } = parseArgs(args, {
|
|
79
|
-
agent: { type: "string" }
|
|
80
|
-
});
|
|
81
|
-
const agentName = resolveAgentName(flags);
|
|
82
|
-
const res = await daemonFetch(`/api/agents/${encodeURIComponent(agentName)}/conversations`);
|
|
83
|
-
if (!res.ok) {
|
|
84
|
-
const data = await res.json();
|
|
85
|
-
console.error(data.error ?? `Failed to list conversations: ${res.status}`);
|
|
86
|
-
process.exit(1);
|
|
87
|
-
}
|
|
88
|
-
const convs = await res.json();
|
|
89
|
-
if (convs.length === 0) {
|
|
90
|
-
console.log("No conversations.");
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
for (const conv of convs) {
|
|
94
|
-
const title = conv.title || "(untitled)";
|
|
95
|
-
console.log(`${conv.id} ${title} ${conv.updated_at}`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
async function sendToConversation(args) {
|
|
99
|
-
const { positional, flags } = parseArgs(args, {
|
|
100
|
-
agent: { type: "string" }
|
|
101
|
-
});
|
|
102
|
-
const conversationId = positional[0];
|
|
103
|
-
const message = positional[1] ?? await readStdin();
|
|
104
|
-
if (!conversationId || !message) {
|
|
105
|
-
console.error('Usage: volute conversation send <id> "<message>" [--agent <name>]');
|
|
106
|
-
console.error(' echo "message" | volute conversation send <id> [--agent <name>]');
|
|
107
|
-
process.exit(1);
|
|
108
|
-
}
|
|
109
|
-
const agentName = resolveAgentName(flags);
|
|
110
|
-
const res = await daemonFetch(`/api/agents/${encodeURIComponent(agentName)}/chat`, {
|
|
111
|
-
method: "POST",
|
|
112
|
-
headers: { "Content-Type": "application/json" },
|
|
113
|
-
body: JSON.stringify({
|
|
114
|
-
message,
|
|
115
|
-
conversationId,
|
|
116
|
-
sender: process.env.VOLUTE_AGENT || userInfo().username
|
|
117
|
-
})
|
|
118
|
-
});
|
|
119
|
-
if (!res.ok) {
|
|
120
|
-
const data = await res.json();
|
|
121
|
-
console.error(data.error ?? `Failed to send message: ${res.status}`);
|
|
122
|
-
process.exit(1);
|
|
123
|
-
}
|
|
124
|
-
if (!res.body) {
|
|
125
|
-
console.error("No response body");
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
const reader = res.body.getReader();
|
|
129
|
-
const decoder = new TextDecoder();
|
|
130
|
-
let buffer = "";
|
|
131
|
-
while (true) {
|
|
132
|
-
const { done, value } = await reader.read();
|
|
133
|
-
if (done) break;
|
|
134
|
-
buffer += decoder.decode(value, { stream: true });
|
|
135
|
-
const lines = buffer.split("\n");
|
|
136
|
-
buffer = lines.pop() ?? "";
|
|
137
|
-
for (const line of lines) {
|
|
138
|
-
if (!line.startsWith("data:")) continue;
|
|
139
|
-
const data = line.slice(5).trim();
|
|
140
|
-
if (!data) continue;
|
|
141
|
-
let event;
|
|
142
|
-
try {
|
|
143
|
-
event = JSON.parse(data);
|
|
144
|
-
} catch {
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
if (event.type === "text") {
|
|
148
|
-
process.stdout.write(event.content);
|
|
149
|
-
} else if (event.type === "tool_use") {
|
|
150
|
-
process.stderr.write(`${summarizeTool(event.name, event.input)}
|
|
151
|
-
`);
|
|
152
|
-
}
|
|
153
|
-
if (event.type === "done") {
|
|
154
|
-
process.stdout.write("\n");
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
process.stdout.write("\n");
|
|
160
|
-
}
|
|
161
|
-
export {
|
|
162
|
-
run
|
|
163
|
-
};
|