@cremini/skillpack 1.0.9 → 1.1.1
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/README.md +4 -4
- package/dist/cli.js +4 -0
- package/package.json +4 -3
- package/runtime/README.md +11 -1
- package/runtime/server/dist/adapters/markdown.js +74 -0
- package/runtime/server/dist/adapters/markdown.js.map +1 -0
- package/runtime/server/dist/adapters/slack.js +369 -0
- package/runtime/server/dist/adapters/slack.js.map +1 -0
- package/runtime/server/dist/adapters/telegram.js +199 -0
- package/runtime/server/dist/adapters/telegram.js.map +1 -0
- package/runtime/server/dist/adapters/types.js +2 -0
- package/runtime/server/dist/adapters/types.js.map +1 -0
- package/runtime/server/dist/adapters/web.js +201 -0
- package/runtime/server/dist/adapters/web.js.map +1 -0
- package/runtime/server/dist/agent.js +223 -0
- package/runtime/server/dist/agent.js.map +1 -0
- package/runtime/server/dist/config.js +79 -0
- package/runtime/server/dist/config.js.map +1 -0
- package/runtime/server/dist/index.js +146 -0
- package/runtime/server/dist/index.js.map +1 -0
- package/runtime/server/dist/lifecycle.js +85 -0
- package/runtime/server/dist/lifecycle.js.map +1 -0
- package/runtime/server/dist/memory.js +195 -0
- package/runtime/server/dist/memory.js.map +1 -0
- package/runtime/server/package-lock.json +4028 -244
- package/runtime/server/package.json +13 -3
- package/runtime/start.bat +40 -4
- package/runtime/start.sh +30 -2
- package/runtime/web/index.html +145 -18
- package/runtime/web/js/api-key-dialog.js +153 -0
- package/runtime/web/js/api.js +25 -0
- package/runtime/web/js/chat-apps-dialog.js +194 -0
- package/runtime/web/{app.js → js/chat.js} +112 -193
- package/runtime/web/js/config.js +16 -0
- package/runtime/web/js/main.js +56 -0
- package/runtime/web/js/settings.js +205 -0
- package/runtime/web/styles.css +311 -10
- package/runtime/server/chat-proxy.js +0 -229
- package/runtime/server/index.js +0 -63
- package/runtime/server/routes.js +0 -104
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import TelegramBot from "node-telegram-bot-api";
|
|
2
|
+
import { formatTelegramMessage } from "./markdown.js";
|
|
3
|
+
const COMMANDS = {
|
|
4
|
+
"/clear": "clear",
|
|
5
|
+
"/restart": "restart",
|
|
6
|
+
"/shutdown": "shutdown",
|
|
7
|
+
};
|
|
8
|
+
const MAX_MESSAGE_LENGTH = 4096;
|
|
9
|
+
const ACK_REACTION = {
|
|
10
|
+
type: "emoji",
|
|
11
|
+
emoji: "👀",
|
|
12
|
+
};
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// TelegramAdapter
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
export class TelegramAdapter {
|
|
17
|
+
name = "telegram";
|
|
18
|
+
bot = null;
|
|
19
|
+
agent = null;
|
|
20
|
+
options;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.options = options;
|
|
23
|
+
}
|
|
24
|
+
async start(ctx) {
|
|
25
|
+
this.agent = ctx.agent;
|
|
26
|
+
this.bot = new TelegramBot(this.options.token, { polling: true });
|
|
27
|
+
this.bot.on("message", (msg) => {
|
|
28
|
+
this.handleTelegramMessage(msg).catch((err) => {
|
|
29
|
+
console.error("[Telegram] Error handling message:", err);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
// Register bot commands with Telegram
|
|
33
|
+
await this.bot.setMyCommands([
|
|
34
|
+
{ command: "clear", description: "Clear current session and start new" },
|
|
35
|
+
{ command: "restart", description: "Restart the server process" },
|
|
36
|
+
{ command: "shutdown", description: "Shut down the server process" },
|
|
37
|
+
]);
|
|
38
|
+
const me = await this.bot.getMe();
|
|
39
|
+
console.log(`[TelegramAdapter] Started as @${me.username}`);
|
|
40
|
+
}
|
|
41
|
+
async stop() {
|
|
42
|
+
if (this.bot) {
|
|
43
|
+
await this.bot.stopPolling();
|
|
44
|
+
this.bot = null;
|
|
45
|
+
}
|
|
46
|
+
console.log("[TelegramAdapter] Stopped");
|
|
47
|
+
}
|
|
48
|
+
// -------------------------------------------------------------------------
|
|
49
|
+
// Message handler
|
|
50
|
+
// -------------------------------------------------------------------------
|
|
51
|
+
async handleTelegramMessage(msg) {
|
|
52
|
+
if (!this.bot || !this.agent)
|
|
53
|
+
return;
|
|
54
|
+
const chatId = msg.chat.id;
|
|
55
|
+
const messageId = msg.message_id;
|
|
56
|
+
const text = msg.text?.trim();
|
|
57
|
+
if (!text)
|
|
58
|
+
return;
|
|
59
|
+
const channelId = `telegram-${chatId}`;
|
|
60
|
+
await this.tryAckReaction(chatId, messageId);
|
|
61
|
+
// --- Command handling ---
|
|
62
|
+
const commandKey = text.split(/\s/)[0].toLowerCase();
|
|
63
|
+
const command = COMMANDS[commandKey];
|
|
64
|
+
if (command) {
|
|
65
|
+
const result = await this.agent.handleCommand(command, channelId);
|
|
66
|
+
await this.sendSafe(chatId, result.message || `/${command} executed.`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// --- Regular message → agent ---
|
|
70
|
+
// Send a "thinking" indicator
|
|
71
|
+
await this.bot.sendChatAction(chatId, "typing");
|
|
72
|
+
let finalText = "";
|
|
73
|
+
let hasError = false;
|
|
74
|
+
let errorMessage = "";
|
|
75
|
+
const onEvent = (event) => {
|
|
76
|
+
// Only collect final text; skip thinking/tool intermediate events
|
|
77
|
+
switch (event.type) {
|
|
78
|
+
case "text_delta":
|
|
79
|
+
finalText += event.delta;
|
|
80
|
+
break;
|
|
81
|
+
// We intentionally ignore thinking_delta, tool_start, tool_end
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
try {
|
|
85
|
+
const result = await this.agent.handleMessage(channelId, text, onEvent);
|
|
86
|
+
if (result.errorMessage) {
|
|
87
|
+
hasError = true;
|
|
88
|
+
errorMessage = result.errorMessage;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
hasError = true;
|
|
93
|
+
errorMessage = String(err);
|
|
94
|
+
}
|
|
95
|
+
// --- Send response ---
|
|
96
|
+
if (hasError) {
|
|
97
|
+
await this.sendSafe(chatId, `❌ Error: ${errorMessage}`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (!finalText.trim()) {
|
|
101
|
+
await this.sendSafe(chatId, "(No response generated)");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// Split and send the final text
|
|
105
|
+
await this.sendLongMessage(chatId, finalText);
|
|
106
|
+
}
|
|
107
|
+
// -------------------------------------------------------------------------
|
|
108
|
+
// Send helpers
|
|
109
|
+
// -------------------------------------------------------------------------
|
|
110
|
+
/**
|
|
111
|
+
* Send a message, splitting into chunks if too long.
|
|
112
|
+
*/
|
|
113
|
+
async sendLongMessage(chatId, text) {
|
|
114
|
+
const chunks = this.splitMessage(text);
|
|
115
|
+
for (const chunk of chunks) {
|
|
116
|
+
await this.sendWithRetry(chatId, chunk);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* React to the incoming message to show the bot has started processing it.
|
|
121
|
+
*/
|
|
122
|
+
async tryAckReaction(chatId, messageId) {
|
|
123
|
+
try {
|
|
124
|
+
await this.bot?.setMessageReaction(chatId, messageId, {
|
|
125
|
+
reaction: [ACK_REACTION],
|
|
126
|
+
is_big: false,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
console.error("[Telegram] Failed to add ack reaction:", err);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Split text into chunks respecting Telegram's message length limit.
|
|
135
|
+
* Tries to split at paragraph boundaries.
|
|
136
|
+
*/
|
|
137
|
+
splitMessage(text) {
|
|
138
|
+
if (text.length <= MAX_MESSAGE_LENGTH) {
|
|
139
|
+
return [text];
|
|
140
|
+
}
|
|
141
|
+
const chunks = [];
|
|
142
|
+
let remaining = text;
|
|
143
|
+
while (remaining.length > 0) {
|
|
144
|
+
if (remaining.length <= MAX_MESSAGE_LENGTH) {
|
|
145
|
+
chunks.push(remaining);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
// Find a good split point (paragraph break, then line break, then space)
|
|
149
|
+
let splitAt = remaining.lastIndexOf("\n\n", MAX_MESSAGE_LENGTH);
|
|
150
|
+
if (splitAt < MAX_MESSAGE_LENGTH * 0.5) {
|
|
151
|
+
splitAt = remaining.lastIndexOf("\n", MAX_MESSAGE_LENGTH);
|
|
152
|
+
}
|
|
153
|
+
if (splitAt < MAX_MESSAGE_LENGTH * 0.3) {
|
|
154
|
+
splitAt = remaining.lastIndexOf(" ", MAX_MESSAGE_LENGTH);
|
|
155
|
+
}
|
|
156
|
+
if (splitAt < 1) {
|
|
157
|
+
splitAt = MAX_MESSAGE_LENGTH;
|
|
158
|
+
}
|
|
159
|
+
chunks.push(remaining.slice(0, splitAt));
|
|
160
|
+
remaining = remaining.slice(splitAt).trimStart();
|
|
161
|
+
}
|
|
162
|
+
return chunks;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Send a message with automatic retry on 429 (rate limit).
|
|
166
|
+
*/
|
|
167
|
+
async sendWithRetry(chatId, text, maxRetries = 3) {
|
|
168
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
169
|
+
try {
|
|
170
|
+
await this.bot.sendMessage(chatId, formatTelegramMessage(text), {
|
|
171
|
+
parse_mode: "HTML",
|
|
172
|
+
});
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
if (err?.response?.statusCode === 429 &&
|
|
177
|
+
attempt < maxRetries) {
|
|
178
|
+
const retryAfter = err.response?.body?.parameters?.retry_after || 5;
|
|
179
|
+
console.log(`[Telegram] Rate limited, retrying after ${retryAfter}s...`);
|
|
180
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
throw err;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Safe send that catches and logs errors.
|
|
189
|
+
*/
|
|
190
|
+
async sendSafe(chatId, text) {
|
|
191
|
+
try {
|
|
192
|
+
await this.sendWithRetry(chatId, text);
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
console.error("[Telegram] Failed to send message:", err);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=telegram.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram.js","sourceRoot":"","sources":["../../src/adapters/telegram.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { WebSocketServer } from "ws";
|
|
4
|
+
import { configManager } from "../config.js";
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Helpers
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
function getPackConfig(rootDir) {
|
|
9
|
+
const raw = fs.readFileSync(path.join(rootDir, "skillpack.json"), "utf-8");
|
|
10
|
+
return JSON.parse(raw);
|
|
11
|
+
}
|
|
12
|
+
const COMMANDS = {
|
|
13
|
+
"/clear": "clear",
|
|
14
|
+
"/restart": "restart",
|
|
15
|
+
"/shutdown": "shutdown",
|
|
16
|
+
};
|
|
17
|
+
function parseCommand(text) {
|
|
18
|
+
const trimmed = text.trim().toLowerCase();
|
|
19
|
+
return COMMANDS[trimmed] ?? null;
|
|
20
|
+
}
|
|
21
|
+
function getRuntimeConfigSignature(config) {
|
|
22
|
+
return JSON.stringify({
|
|
23
|
+
apiKey: config.apiKey || "",
|
|
24
|
+
provider: config.provider || "openai",
|
|
25
|
+
telegramToken: config.adapters?.telegram?.token || "",
|
|
26
|
+
slackBotToken: config.adapters?.slack?.botToken || "",
|
|
27
|
+
slackAppToken: config.adapters?.slack?.appToken || "",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// WebAdapter
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
export class WebAdapter {
|
|
34
|
+
name = "web";
|
|
35
|
+
wss = null;
|
|
36
|
+
agent = null;
|
|
37
|
+
async start(ctx) {
|
|
38
|
+
const { agent, server, app, rootDir, lifecycle } = ctx;
|
|
39
|
+
this.agent = agent;
|
|
40
|
+
// -- API key & provider (in-memory, can be overridden by frontend) ------
|
|
41
|
+
const currentConf = configManager.getConfig();
|
|
42
|
+
let apiKey = currentConf.apiKey || "";
|
|
43
|
+
let currentProvider = currentConf.provider || "openai";
|
|
44
|
+
// -- HTTP API routes ----------------------------------------------------
|
|
45
|
+
app.get("/api/config", (_req, res) => {
|
|
46
|
+
const config = getPackConfig(rootDir);
|
|
47
|
+
const conf = configManager.getConfig();
|
|
48
|
+
res.json({
|
|
49
|
+
name: config.name,
|
|
50
|
+
description: config.description,
|
|
51
|
+
prompts: config.prompts || [],
|
|
52
|
+
skills: config.skills || [],
|
|
53
|
+
hasApiKey: !!conf.apiKey,
|
|
54
|
+
apiKey: conf.apiKey || "",
|
|
55
|
+
provider: conf.provider || "openai",
|
|
56
|
+
adapters: conf.adapters || {},
|
|
57
|
+
runtimeControl: lifecycle.getRuntimeControl(),
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
app.get("/api/skills", (_req, res) => {
|
|
61
|
+
const config = getPackConfig(rootDir);
|
|
62
|
+
res.json(config.skills || []);
|
|
63
|
+
});
|
|
64
|
+
app.post("/api/config/update", (req, res) => {
|
|
65
|
+
const { key, provider, adapters } = req.body;
|
|
66
|
+
const updates = {};
|
|
67
|
+
const beforeConfig = JSON.parse(JSON.stringify(configManager.getConfig()));
|
|
68
|
+
if (key !== undefined) {
|
|
69
|
+
updates.apiKey = key;
|
|
70
|
+
apiKey = key;
|
|
71
|
+
}
|
|
72
|
+
if (provider !== undefined) {
|
|
73
|
+
updates.provider = provider;
|
|
74
|
+
currentProvider = provider;
|
|
75
|
+
}
|
|
76
|
+
if (adapters !== undefined) {
|
|
77
|
+
updates.adapters = adapters;
|
|
78
|
+
}
|
|
79
|
+
configManager.save(rootDir, updates);
|
|
80
|
+
// Note: PackAgent instances need to be recreated or have their keys updated dynamically,
|
|
81
|
+
// but if the design is to restart to take effect or if we only need it persisted, this covers the save.
|
|
82
|
+
// Depending on agent implementation, we might need agent.updateConfig({ apiKey: key, provider: currentProvider })
|
|
83
|
+
const newConf = configManager.getConfig();
|
|
84
|
+
const requiresRestart = getRuntimeConfigSignature(beforeConfig) !==
|
|
85
|
+
getRuntimeConfigSignature(newConf);
|
|
86
|
+
res.json({
|
|
87
|
+
success: true,
|
|
88
|
+
provider: newConf.provider,
|
|
89
|
+
adapters: newConf.adapters,
|
|
90
|
+
requiresRestart,
|
|
91
|
+
runtimeControl: lifecycle.getRuntimeControl(),
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
app.post("/api/runtime/restart", async (_req, res) => {
|
|
95
|
+
const runtimeControl = lifecycle.getRuntimeControl();
|
|
96
|
+
if (!runtimeControl.canManagedRestart) {
|
|
97
|
+
res.status(409).json({
|
|
98
|
+
success: false,
|
|
99
|
+
message: "Managed restart is unavailable for this process.",
|
|
100
|
+
runtimeControl,
|
|
101
|
+
});
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const result = await lifecycle.requestRestart("web");
|
|
105
|
+
res.status(202).json({ ...result, runtimeControl });
|
|
106
|
+
});
|
|
107
|
+
app.delete("/api/chat", (_req, res) => {
|
|
108
|
+
res.json({ success: true });
|
|
109
|
+
});
|
|
110
|
+
// -- Reserved: session history endpoints (stub) -------------------------
|
|
111
|
+
app.get("/api/sessions", (_req, res) => {
|
|
112
|
+
const sessions = agent.listSessions();
|
|
113
|
+
res.json(sessions);
|
|
114
|
+
});
|
|
115
|
+
app.get("/api/sessions/:id", (_req, res) => {
|
|
116
|
+
// TODO: restore session by id
|
|
117
|
+
res.status(501).json({ error: "Not implemented yet" });
|
|
118
|
+
});
|
|
119
|
+
// -- WebSocket ----------------------------------------------------------
|
|
120
|
+
this.wss = new WebSocketServer({ noServer: true });
|
|
121
|
+
server.on("upgrade", (request, socket, head) => {
|
|
122
|
+
if (request.url?.startsWith("/api/chat")) {
|
|
123
|
+
this.wss.handleUpgrade(request, socket, head, (ws) => {
|
|
124
|
+
this.wss.emit("connection", ws, request);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
socket.destroy();
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
this.wss.on("connection", (ws, request) => {
|
|
132
|
+
const url = new URL(request.url ?? "/", `http://${request.headers.host || "127.0.0.1"}`);
|
|
133
|
+
const _reqProvider = url.searchParams.get("provider") || currentProvider;
|
|
134
|
+
if (!apiKey) {
|
|
135
|
+
ws.send(JSON.stringify({ error: "Please set an API key first" }));
|
|
136
|
+
ws.close();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// Each WebSocket connection maps to a unique channel
|
|
140
|
+
const channelId = `web-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
141
|
+
this.handleWsConnection(ws, channelId, agent);
|
|
142
|
+
});
|
|
143
|
+
console.log("[WebAdapter] Started");
|
|
144
|
+
}
|
|
145
|
+
async stop() {
|
|
146
|
+
if (this.wss) {
|
|
147
|
+
for (const client of this.wss.clients) {
|
|
148
|
+
client.close();
|
|
149
|
+
}
|
|
150
|
+
this.wss.close();
|
|
151
|
+
this.wss = null;
|
|
152
|
+
}
|
|
153
|
+
console.log("[WebAdapter] Stopped");
|
|
154
|
+
}
|
|
155
|
+
// -------------------------------------------------------------------------
|
|
156
|
+
// WebSocket message handler
|
|
157
|
+
// -------------------------------------------------------------------------
|
|
158
|
+
handleWsConnection(ws, channelId, agent) {
|
|
159
|
+
ws.on("message", async (data) => {
|
|
160
|
+
try {
|
|
161
|
+
const payload = JSON.parse(data.toString());
|
|
162
|
+
if (!payload.text)
|
|
163
|
+
return;
|
|
164
|
+
const text = payload.text;
|
|
165
|
+
// Check for bot commands
|
|
166
|
+
const command = parseCommand(text);
|
|
167
|
+
if (command) {
|
|
168
|
+
const result = await agent.handleCommand(command, channelId);
|
|
169
|
+
ws.send(JSON.stringify({
|
|
170
|
+
type: "command_result",
|
|
171
|
+
command,
|
|
172
|
+
...result,
|
|
173
|
+
}));
|
|
174
|
+
if (command === "clear") {
|
|
175
|
+
ws.send(JSON.stringify({ done: true }));
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
// Regular message → stream events via WebSocket
|
|
180
|
+
const onEvent = (event) => {
|
|
181
|
+
if (ws.readyState !== ws.OPEN)
|
|
182
|
+
return;
|
|
183
|
+
ws.send(JSON.stringify(event));
|
|
184
|
+
};
|
|
185
|
+
const result = await agent.handleMessage(channelId, text, onEvent);
|
|
186
|
+
if (result.errorMessage) {
|
|
187
|
+
ws.send(JSON.stringify({ error: result.errorMessage }));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
ws.send(JSON.stringify({ done: true }));
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
ws.send(JSON.stringify({ error: String(err) }));
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
ws.on("close", () => {
|
|
197
|
+
agent.dispose(channelId);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=web.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/adapters/web.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { AuthStorage, createAgentSession, ModelRegistry, SessionManager, DefaultResourceLoader, } from "@mariozechner/pi-coding-agent";
|
|
3
|
+
const DEBUG = true;
|
|
4
|
+
const log = (...args) => DEBUG && console.log(...args);
|
|
5
|
+
const write = (data) => DEBUG && process.stdout.write(data);
|
|
6
|
+
function getAssistantDiagnostics(message) {
|
|
7
|
+
if (!message || message.role !== "assistant") {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const stopReason = message.stopReason ?? "unknown";
|
|
11
|
+
const errorMessage = message.errorMessage ||
|
|
12
|
+
(stopReason === "error" || stopReason === "aborted"
|
|
13
|
+
? `Request ${stopReason}`
|
|
14
|
+
: "");
|
|
15
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
16
|
+
const text = content
|
|
17
|
+
.filter((item) => item?.type === "text")
|
|
18
|
+
.map((item) => item.text || "")
|
|
19
|
+
.join("")
|
|
20
|
+
.trim();
|
|
21
|
+
const toolCalls = content.filter((item) => item?.type === "toolCall").length;
|
|
22
|
+
return { stopReason, errorMessage, hasText: text.length > 0, toolCalls };
|
|
23
|
+
}
|
|
24
|
+
function getLifecycleTrigger(channelId) {
|
|
25
|
+
if (channelId.startsWith("telegram-"))
|
|
26
|
+
return "telegram";
|
|
27
|
+
if (channelId.startsWith("slack-"))
|
|
28
|
+
return "slack";
|
|
29
|
+
return "web";
|
|
30
|
+
}
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// PackAgent
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
export class PackAgent {
|
|
35
|
+
options;
|
|
36
|
+
channels = new Map();
|
|
37
|
+
constructor(options) {
|
|
38
|
+
this.options = options;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Lazily create (or return existing) session for a channel.
|
|
42
|
+
*/
|
|
43
|
+
async getOrCreateSession(channelId) {
|
|
44
|
+
const existing = this.channels.get(channelId);
|
|
45
|
+
if (existing)
|
|
46
|
+
return existing;
|
|
47
|
+
const { apiKey, rootDir, provider, modelId } = this.options;
|
|
48
|
+
const authStorage = AuthStorage.inMemory({
|
|
49
|
+
[provider]: { type: "api_key", key: apiKey },
|
|
50
|
+
});
|
|
51
|
+
authStorage.setRuntimeApiKey(provider, apiKey);
|
|
52
|
+
const modelRegistry = new ModelRegistry(authStorage);
|
|
53
|
+
const model = modelRegistry.find(provider, modelId);
|
|
54
|
+
const sessionManager = SessionManager.inMemory();
|
|
55
|
+
const skillsPath = path.resolve(rootDir, "skills");
|
|
56
|
+
log(`[PackAgent] Loading skills from: ${skillsPath}`);
|
|
57
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
58
|
+
cwd: rootDir,
|
|
59
|
+
additionalSkillPaths: [skillsPath],
|
|
60
|
+
});
|
|
61
|
+
await resourceLoader.reload();
|
|
62
|
+
const { session } = await createAgentSession({
|
|
63
|
+
cwd: rootDir,
|
|
64
|
+
authStorage,
|
|
65
|
+
modelRegistry,
|
|
66
|
+
sessionManager,
|
|
67
|
+
resourceLoader,
|
|
68
|
+
model,
|
|
69
|
+
});
|
|
70
|
+
const channelSession = { session, running: false };
|
|
71
|
+
this.channels.set(channelId, channelSession);
|
|
72
|
+
return channelSession;
|
|
73
|
+
}
|
|
74
|
+
async handleMessage(channelId, text, onEvent) {
|
|
75
|
+
const cs = await this.getOrCreateSession(channelId);
|
|
76
|
+
cs.running = true;
|
|
77
|
+
let turnHadVisibleOutput = false;
|
|
78
|
+
// Subscribe to agent events and forward to adapter
|
|
79
|
+
const unsubscribe = cs.session.subscribe((event) => {
|
|
80
|
+
switch (event.type) {
|
|
81
|
+
case "agent_start":
|
|
82
|
+
log("\n=== [AGENT SESSION START] ===");
|
|
83
|
+
log("System Prompt:\n", cs.session.systemPrompt);
|
|
84
|
+
log("============================\n");
|
|
85
|
+
onEvent({ type: "agent_start" });
|
|
86
|
+
break;
|
|
87
|
+
case "message_start":
|
|
88
|
+
log(`\n--- [Message Start: ${event.message?.role}] ---`);
|
|
89
|
+
if (event.message?.role === "user") {
|
|
90
|
+
log(JSON.stringify(event.message.content, null, 2));
|
|
91
|
+
}
|
|
92
|
+
onEvent({ type: "message_start", role: event.message?.role ?? "" });
|
|
93
|
+
break;
|
|
94
|
+
case "message_update":
|
|
95
|
+
if (event.assistantMessageEvent?.type === "text_delta") {
|
|
96
|
+
turnHadVisibleOutput = true;
|
|
97
|
+
write(event.assistantMessageEvent.delta);
|
|
98
|
+
onEvent({
|
|
99
|
+
type: "text_delta",
|
|
100
|
+
delta: event.assistantMessageEvent.delta,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
else if (event.assistantMessageEvent?.type === "thinking_delta") {
|
|
104
|
+
turnHadVisibleOutput = true;
|
|
105
|
+
onEvent({
|
|
106
|
+
type: "thinking_delta",
|
|
107
|
+
delta: event.assistantMessageEvent.delta,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
case "message_end":
|
|
112
|
+
log(`\n--- [Message End: ${event.message?.role}] ---`);
|
|
113
|
+
if (event.message?.role === "assistant") {
|
|
114
|
+
const diagnostics = getAssistantDiagnostics(event.message);
|
|
115
|
+
if (diagnostics) {
|
|
116
|
+
log(`[Assistant Diagnostics] stopReason=${diagnostics.stopReason} text=${diagnostics.hasText ? "yes" : "no"} toolCalls=${diagnostics.toolCalls}`);
|
|
117
|
+
if (diagnostics.errorMessage) {
|
|
118
|
+
log(`[Assistant Error] ${diagnostics.errorMessage}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
onEvent({ type: "message_end", role: event.message?.role ?? "" });
|
|
123
|
+
break;
|
|
124
|
+
case "tool_execution_start":
|
|
125
|
+
turnHadVisibleOutput = true;
|
|
126
|
+
log(`\n>>> [Tool Start: ${event.toolName}] >>>`);
|
|
127
|
+
log("Args:", JSON.stringify(event.args, null, 2));
|
|
128
|
+
onEvent({
|
|
129
|
+
type: "tool_start",
|
|
130
|
+
toolName: event.toolName,
|
|
131
|
+
toolInput: event.args,
|
|
132
|
+
});
|
|
133
|
+
break;
|
|
134
|
+
case "tool_execution_end":
|
|
135
|
+
turnHadVisibleOutput = true;
|
|
136
|
+
log(`<<< [Tool End: ${event.toolName}] <<<`);
|
|
137
|
+
log(`Error: ${event.isError ? "Yes" : "No"}`);
|
|
138
|
+
onEvent({
|
|
139
|
+
type: "tool_end",
|
|
140
|
+
toolName: event.toolName,
|
|
141
|
+
isError: event.isError,
|
|
142
|
+
result: event.result,
|
|
143
|
+
});
|
|
144
|
+
break;
|
|
145
|
+
case "agent_end":
|
|
146
|
+
log("\n=== [AGENT SESSION END] ===\n");
|
|
147
|
+
onEvent({ type: "agent_end" });
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
try {
|
|
152
|
+
await cs.session.prompt(text);
|
|
153
|
+
const lastMessage = cs.session.state.messages.at(-1);
|
|
154
|
+
const diagnostics = getAssistantDiagnostics(lastMessage);
|
|
155
|
+
if (diagnostics?.errorMessage) {
|
|
156
|
+
return {
|
|
157
|
+
stopReason: diagnostics.stopReason,
|
|
158
|
+
errorMessage: diagnostics.errorMessage,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (diagnostics &&
|
|
162
|
+
!diagnostics.hasText &&
|
|
163
|
+
diagnostics.toolCalls === 0 &&
|
|
164
|
+
!turnHadVisibleOutput) {
|
|
165
|
+
return {
|
|
166
|
+
stopReason: diagnostics.stopReason,
|
|
167
|
+
errorMessage: "Assistant returned no visible output. Check the server logs for details.",
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
return { stopReason: diagnostics?.stopReason ?? "unknown" };
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
cs.running = false;
|
|
174
|
+
unsubscribe();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async handleCommand(command, channelId) {
|
|
178
|
+
switch (command) {
|
|
179
|
+
case "clear": {
|
|
180
|
+
const cs = this.channels.get(channelId);
|
|
181
|
+
if (cs) {
|
|
182
|
+
cs.session.dispose();
|
|
183
|
+
this.channels.delete(channelId);
|
|
184
|
+
}
|
|
185
|
+
return { success: true, message: "Session cleared." };
|
|
186
|
+
}
|
|
187
|
+
case "restart":
|
|
188
|
+
log("[PackAgent] Restart requested");
|
|
189
|
+
return this.options.lifecycleHandler.requestRestart(getLifecycleTrigger(channelId));
|
|
190
|
+
case "shutdown":
|
|
191
|
+
log("[PackAgent] Shutdown requested");
|
|
192
|
+
return this.options.lifecycleHandler.requestShutdown(getLifecycleTrigger(channelId));
|
|
193
|
+
default:
|
|
194
|
+
return { success: false, message: `Unknown command: ${command}` };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
abort(channelId) {
|
|
198
|
+
const cs = this.channels.get(channelId);
|
|
199
|
+
if (cs?.running) {
|
|
200
|
+
cs.session.abort?.();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
isRunning(channelId) {
|
|
204
|
+
return this.channels.get(channelId)?.running ?? false;
|
|
205
|
+
}
|
|
206
|
+
dispose(channelId) {
|
|
207
|
+
const cs = this.channels.get(channelId);
|
|
208
|
+
if (cs) {
|
|
209
|
+
cs.session.dispose();
|
|
210
|
+
this.channels.delete(channelId);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/** Reserved: list all sessions */
|
|
214
|
+
listSessions() {
|
|
215
|
+
// TODO: Implement session persistence and listing
|
|
216
|
+
return [];
|
|
217
|
+
}
|
|
218
|
+
/** Reserved: restore a historical session */
|
|
219
|
+
async restoreSession(_sessionId) {
|
|
220
|
+
// TODO: Implement session restoration
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=agent.js.map
|