@tractorscorch/clank 1.7.0 → 1.7.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/CHANGELOG.md +21 -0
- package/README.md +2 -2
- package/dist/index.js +259 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [1.7.1] — 2026-03-23
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Telegram bot menu** — all commands registered with Telegram via `setMyCommands()`, appear when you type `/`
|
|
13
|
+
- **Tool indicators with emojis** — when the agent uses tools, Telegram shows emoji indicators above the response (📄 read\_file, 💻 bash, 🔍 search, 🌐 web, ✏️ edit, 🚀 spawn, etc.)
|
|
14
|
+
- **`/kill <id>` command** — kill a specific background task by short ID (first 8 chars), cascades to children
|
|
15
|
+
- **`/killall` command** — kill all running background tasks
|
|
16
|
+
- **`/version` command** — show Clank version
|
|
17
|
+
- **`/think` per-chat toggle** — actually toggles thinking display per Telegram chat (was a no-op before)
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- **`/tasks` shows short IDs** — each task now shows its 8-char ID for use with `/kill`
|
|
21
|
+
- **`/status` shows more info** — model, agents, running tasks, thinking state, uptime
|
|
22
|
+
- **`/agents` shows default agent** — includes the default agent and its model, not just custom agents
|
|
23
|
+
- **`/model` shows fallbacks** — displays the full fallback chain
|
|
24
|
+
- **`/agent <name>` works** — actually switches agent by resetting session (was a stub)
|
|
25
|
+
- **Comprehensive docs** — full rewrite of Install Guide and User Guide for v1.7.x features
|
|
26
|
+
- **Website docs page** — new /docs tab on clanksuite.dev with provider table, command reference, and quick start
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
9
30
|
## [1.7.0] — 2026-03-23
|
|
10
31
|
|
|
11
32
|
### Added
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<a href="https://github.com/ItsTrag1c/Clank/releases/latest"><img src="https://img.shields.io/badge/version-1.7.
|
|
12
|
+
<a href="https://github.com/ItsTrag1c/Clank/releases/latest"><img src="https://img.shields.io/badge/version-1.7.1-blue.svg" alt="Version" /></a>
|
|
13
13
|
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License" /></a>
|
|
14
14
|
<a href="https://www.npmjs.com/package/@tractorscorch/clank"><img src="https://img.shields.io/npm/v/@tractorscorch/clank.svg" alt="npm" /></a>
|
|
15
15
|
<a href="https://github.com/ItsTrag1c/Clank/stargazers"><img src="https://img.shields.io/github/stars/ItsTrag1c/Clank.svg" alt="Stars" /></a>
|
|
@@ -75,7 +75,7 @@ That's it. Setup auto-detects your local models, configures the gateway, and get
|
|
|
75
75
|
| Platform | Download |
|
|
76
76
|
|----------|----------|
|
|
77
77
|
| **npm** (all platforms) | `npm install -g @tractorscorch/clank` |
|
|
78
|
-
| **macOS** (Apple Silicon) | [Clank_1.7.
|
|
78
|
+
| **macOS** (Apple Silicon) | [Clank_1.7.1_macos](https://github.com/ItsTrag1c/Clank/releases/latest/download/Clank_1.7.1_macos) |
|
|
79
79
|
|
|
80
80
|
## Security Notice
|
|
81
81
|
|
package/dist/index.js
CHANGED
|
@@ -6085,6 +6085,67 @@ var init_base = __esm({
|
|
|
6085
6085
|
|
|
6086
6086
|
// src/adapters/telegram.ts
|
|
6087
6087
|
import { Bot } from "grammy";
|
|
6088
|
+
function toolEmoji(name) {
|
|
6089
|
+
const map = {
|
|
6090
|
+
read_file: "\u{1F4C4}",
|
|
6091
|
+
write_file: "\u270F\uFE0F",
|
|
6092
|
+
edit_file: "\u270F\uFE0F",
|
|
6093
|
+
list_directory: "\u{1F4C1}",
|
|
6094
|
+
search_files: "\u{1F50D}",
|
|
6095
|
+
glob_files: "\u{1F50D}",
|
|
6096
|
+
bash: "\u{1F4BB}",
|
|
6097
|
+
git: "\u{1F4E6}",
|
|
6098
|
+
web_search: "\u{1F310}",
|
|
6099
|
+
web_fetch: "\u{1F310}",
|
|
6100
|
+
spawn_task: "\u{1F680}",
|
|
6101
|
+
manage_agent: "\u{1F916}",
|
|
6102
|
+
manage_model: "\u{1F9E0}",
|
|
6103
|
+
manage_config: "\u2699\uFE0F",
|
|
6104
|
+
manage_session: "\u{1F4CB}",
|
|
6105
|
+
manage_cron: "\u23F0",
|
|
6106
|
+
tts: "\u{1F50A}",
|
|
6107
|
+
stt: "\u{1F3A4}"
|
|
6108
|
+
};
|
|
6109
|
+
return map[name] || "\u{1F527}";
|
|
6110
|
+
}
|
|
6111
|
+
function formatTool(name, done) {
|
|
6112
|
+
const emoji = toolEmoji(name);
|
|
6113
|
+
if (done === void 0) return `${emoji} ${name}`;
|
|
6114
|
+
return done ? `${emoji} ${name} \u2713` : `${emoji} ${name} \u2717`;
|
|
6115
|
+
}
|
|
6116
|
+
function buildStreamDisplay(response, thinking, tools, showThinking) {
|
|
6117
|
+
const parts = [];
|
|
6118
|
+
if (showThinking && thinking) {
|
|
6119
|
+
const truncated = thinking.length > 500 ? thinking.slice(-450) + "..." : thinking;
|
|
6120
|
+
parts.push(`\u{1F4AD} ${truncated}`);
|
|
6121
|
+
parts.push("");
|
|
6122
|
+
}
|
|
6123
|
+
if (tools.length > 0) {
|
|
6124
|
+
const toolLine = tools.map((t) => {
|
|
6125
|
+
if (t.done === void 0) return `${toolEmoji(t.name)} ${t.name}...`;
|
|
6126
|
+
return formatTool(t.name, t.done);
|
|
6127
|
+
}).join(" ");
|
|
6128
|
+
parts.push(toolLine);
|
|
6129
|
+
parts.push("");
|
|
6130
|
+
}
|
|
6131
|
+
parts.push(response);
|
|
6132
|
+
return parts.join("\n");
|
|
6133
|
+
}
|
|
6134
|
+
function buildFinalDisplay(response, thinking, tools, showThinking) {
|
|
6135
|
+
const parts = [];
|
|
6136
|
+
if (showThinking && thinking) {
|
|
6137
|
+
const truncated = thinking.length > 1e3 ? thinking.slice(0, 950) + "..." : thinking;
|
|
6138
|
+
parts.push(`\u{1F4AD} _${truncated}_`);
|
|
6139
|
+
parts.push("");
|
|
6140
|
+
}
|
|
6141
|
+
if (tools.length > 0) {
|
|
6142
|
+
const toolLine = tools.map((t) => formatTool(t.name, t.done ?? true)).join(" ");
|
|
6143
|
+
parts.push(toolLine);
|
|
6144
|
+
parts.push("");
|
|
6145
|
+
}
|
|
6146
|
+
parts.push(response);
|
|
6147
|
+
return parts.join("\n");
|
|
6148
|
+
}
|
|
6088
6149
|
function splitMessage(text, maxLen) {
|
|
6089
6150
|
if (text.length <= maxLen) return [text];
|
|
6090
6151
|
const chunks = [];
|
|
@@ -6101,12 +6162,13 @@ function splitMessage(text, maxLen) {
|
|
|
6101
6162
|
}
|
|
6102
6163
|
return chunks;
|
|
6103
6164
|
}
|
|
6104
|
-
var TelegramAdapter;
|
|
6165
|
+
var thinkingEnabled, TelegramAdapter;
|
|
6105
6166
|
var init_telegram = __esm({
|
|
6106
6167
|
"src/adapters/telegram.ts"() {
|
|
6107
6168
|
"use strict";
|
|
6108
6169
|
init_esm_shims();
|
|
6109
6170
|
init_base();
|
|
6171
|
+
thinkingEnabled = /* @__PURE__ */ new Map();
|
|
6110
6172
|
TelegramAdapter = class extends ChannelAdapter {
|
|
6111
6173
|
id = "telegram";
|
|
6112
6174
|
name = "Telegram";
|
|
@@ -6114,6 +6176,7 @@ var init_telegram = __esm({
|
|
|
6114
6176
|
config = null;
|
|
6115
6177
|
bot = null;
|
|
6116
6178
|
running = false;
|
|
6179
|
+
startedAt = 0;
|
|
6117
6180
|
init(gateway2, config) {
|
|
6118
6181
|
this.gateway = gateway2;
|
|
6119
6182
|
this.config = config;
|
|
@@ -6124,9 +6187,25 @@ var init_telegram = __esm({
|
|
|
6124
6187
|
console.log(" Telegram: disabled or no bot token configured");
|
|
6125
6188
|
return;
|
|
6126
6189
|
}
|
|
6190
|
+
this.startedAt = Date.now();
|
|
6127
6191
|
try {
|
|
6128
6192
|
this.bot = new Bot(telegramConfig.botToken);
|
|
6129
6193
|
const bot = this.bot;
|
|
6194
|
+
await bot.api.setMyCommands([
|
|
6195
|
+
{ command: "help", description: "Show available commands" },
|
|
6196
|
+
{ command: "new", description: "Start a new session" },
|
|
6197
|
+
{ command: "reset", description: "Clear current session" },
|
|
6198
|
+
{ command: "status", description: "Agent status and info" },
|
|
6199
|
+
{ command: "agents", description: "List available agents" },
|
|
6200
|
+
{ command: "tasks", description: "Show background tasks" },
|
|
6201
|
+
{ command: "kill", description: "Kill a background task" },
|
|
6202
|
+
{ command: "killall", description: "Kill all running tasks" },
|
|
6203
|
+
{ command: "model", description: "Show current model" },
|
|
6204
|
+
{ command: "sessions", description: "List recent sessions" },
|
|
6205
|
+
{ command: "think", description: "Toggle thinking display" },
|
|
6206
|
+
{ command: "version", description: "Show Clank version" }
|
|
6207
|
+
]).catch(() => {
|
|
6208
|
+
});
|
|
6130
6209
|
const startupTime = Math.floor(Date.now() / 1e3);
|
|
6131
6210
|
const chatLocks = /* @__PURE__ */ new Map();
|
|
6132
6211
|
bot.on("message:text", async (ctx) => {
|
|
@@ -6166,15 +6245,18 @@ var init_telegram = __esm({
|
|
|
6166
6245
|
try {
|
|
6167
6246
|
console.log(` Telegram: processing message from ${userId} in ${chatId}`);
|
|
6168
6247
|
await ctx.api.sendChatAction(chatId, "typing");
|
|
6169
|
-
const
|
|
6248
|
+
const typingInterval = setInterval(() => {
|
|
6170
6249
|
bot.api.sendChatAction(chatId, "typing").catch(() => {
|
|
6171
6250
|
});
|
|
6172
6251
|
}, 4e3);
|
|
6173
6252
|
let streamMsgId = null;
|
|
6174
6253
|
let sendingInitial = false;
|
|
6175
6254
|
let accumulated = "";
|
|
6255
|
+
let thinkingText = "";
|
|
6176
6256
|
let lastEditTime = 0;
|
|
6177
6257
|
const EDIT_INTERVAL = 800;
|
|
6258
|
+
const showThinking = thinkingEnabled.get(chatId) ?? false;
|
|
6259
|
+
let toolIndicators = [];
|
|
6178
6260
|
const response = await this.gateway.handleInboundMessageStreaming(
|
|
6179
6261
|
{
|
|
6180
6262
|
channel: "telegram",
|
|
@@ -6188,7 +6270,8 @@ var init_telegram = __esm({
|
|
|
6188
6270
|
const now = Date.now();
|
|
6189
6271
|
if (!streamMsgId && !sendingInitial && accumulated.length > 20) {
|
|
6190
6272
|
sendingInitial = true;
|
|
6191
|
-
|
|
6273
|
+
const display = buildStreamDisplay(accumulated, thinkingText, toolIndicators, showThinking);
|
|
6274
|
+
bot.api.sendMessage(chatId, display + " \u258D").then((sent) => {
|
|
6192
6275
|
streamMsgId = sent.message_id;
|
|
6193
6276
|
lastEditTime = now;
|
|
6194
6277
|
}).catch(() => {
|
|
@@ -6197,19 +6280,32 @@ var init_telegram = __esm({
|
|
|
6197
6280
|
}
|
|
6198
6281
|
if (streamMsgId && now - lastEditTime > EDIT_INTERVAL) {
|
|
6199
6282
|
lastEditTime = now;
|
|
6200
|
-
const display = accumulated
|
|
6201
|
-
|
|
6283
|
+
const display = buildStreamDisplay(accumulated, thinkingText, toolIndicators, showThinking);
|
|
6284
|
+
const truncated = display.length > 4e3 ? display.slice(-3900) + " \u258D" : display + " \u258D";
|
|
6285
|
+
bot.api.editMessageText(chatId, streamMsgId, truncated).catch(() => {
|
|
6202
6286
|
});
|
|
6203
6287
|
}
|
|
6204
6288
|
},
|
|
6289
|
+
onThinking: (content) => {
|
|
6290
|
+
thinkingText += content;
|
|
6291
|
+
},
|
|
6205
6292
|
onToolStart: (name) => {
|
|
6206
|
-
|
|
6293
|
+
toolIndicators.push({ name });
|
|
6294
|
+
if (streamMsgId) {
|
|
6295
|
+
const display = buildStreamDisplay(accumulated, thinkingText, toolIndicators, showThinking);
|
|
6296
|
+
bot.api.editMessageText(chatId, streamMsgId, display + " \u258D").catch(() => {
|
|
6297
|
+
});
|
|
6298
|
+
} else {
|
|
6207
6299
|
bot.api.sendChatAction(chatId, "typing").catch(() => {
|
|
6208
6300
|
});
|
|
6209
6301
|
}
|
|
6210
6302
|
},
|
|
6303
|
+
onToolResult: (name, success) => {
|
|
6304
|
+
const tool = toolIndicators.find((t) => t.name === name && t.done === void 0);
|
|
6305
|
+
if (tool) tool.done = success;
|
|
6306
|
+
},
|
|
6211
6307
|
onError: (message) => {
|
|
6212
|
-
bot.api.sendMessage(chatId,
|
|
6308
|
+
bot.api.sendMessage(chatId, `\u26A0\uFE0F ${message.slice(0, 200)}`).catch(() => {
|
|
6213
6309
|
});
|
|
6214
6310
|
}
|
|
6215
6311
|
}
|
|
@@ -6229,22 +6325,23 @@ var init_telegram = __esm({
|
|
|
6229
6325
|
});
|
|
6230
6326
|
}
|
|
6231
6327
|
if (streamMsgId && response) {
|
|
6232
|
-
const
|
|
6328
|
+
const display = buildFinalDisplay(response, thinkingText, toolIndicators, showThinking);
|
|
6329
|
+
const finalText = display.length > 4e3 ? display.slice(0, 3950) + "\n... (truncated)" : display;
|
|
6233
6330
|
await bot.api.editMessageText(chatId, streamMsgId, finalText).catch(() => {
|
|
6234
6331
|
});
|
|
6235
6332
|
} else if (response && !streamMsgId) {
|
|
6236
|
-
const
|
|
6333
|
+
const display = buildFinalDisplay(response, thinkingText, toolIndicators, showThinking);
|
|
6334
|
+
const chunks = splitMessage(display, 4e3);
|
|
6237
6335
|
for (const chunk of chunks) {
|
|
6238
6336
|
await ctx.api.sendMessage(chatId, chunk);
|
|
6239
6337
|
}
|
|
6240
6338
|
}
|
|
6241
|
-
clearInterval(
|
|
6339
|
+
clearInterval(typingInterval);
|
|
6242
6340
|
console.log(` Telegram: response complete (${response?.length || 0} chars)`);
|
|
6243
6341
|
} catch (err) {
|
|
6244
|
-
clearInterval(typingInterval);
|
|
6245
6342
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
6246
6343
|
console.error(` Telegram: message handler error \u2014 ${errMsg}`);
|
|
6247
|
-
await ctx.api.sendMessage(chatId,
|
|
6344
|
+
await ctx.api.sendMessage(chatId, `\u26A0\uFE0F Error: ${errMsg.slice(0, 200)}`).catch(() => {
|
|
6248
6345
|
});
|
|
6249
6346
|
}
|
|
6250
6347
|
};
|
|
@@ -6276,7 +6373,7 @@ var init_telegram = __esm({
|
|
|
6276
6373
|
const fileUrl = `https://api.telegram.org/file/bot${telegramConfig.botToken}/${file.file_path}`;
|
|
6277
6374
|
const res = await fetch(fileUrl);
|
|
6278
6375
|
if (!res.ok) {
|
|
6279
|
-
await ctx.api.sendMessage(chatId, "
|
|
6376
|
+
await ctx.api.sendMessage(chatId, "\u26A0\uFE0F Could not download voice message");
|
|
6280
6377
|
return;
|
|
6281
6378
|
}
|
|
6282
6379
|
const audioBuffer = Buffer.from(await res.arrayBuffer());
|
|
@@ -6285,7 +6382,7 @@ var init_telegram = __esm({
|
|
|
6285
6382
|
const config = await loadConfig2();
|
|
6286
6383
|
const stt = new STTEngine2(config);
|
|
6287
6384
|
if (!stt.isAvailable()) {
|
|
6288
|
-
await ctx.api.sendMessage(chatId, "Voice messages require speech-to-text.
|
|
6385
|
+
await ctx.api.sendMessage(chatId, "Voice messages require speech-to-text. Configure Whisper in settings.");
|
|
6289
6386
|
return;
|
|
6290
6387
|
}
|
|
6291
6388
|
const transcription = await stt.transcribe(audioBuffer, "ogg");
|
|
@@ -6317,7 +6414,7 @@ var init_telegram = __esm({
|
|
|
6317
6414
|
}
|
|
6318
6415
|
} catch (err) {
|
|
6319
6416
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
6320
|
-
await ctx.api.sendMessage(chatId,
|
|
6417
|
+
await ctx.api.sendMessage(chatId, `\u26A0\uFE0F Error: ${errMsg.slice(0, 200)}`);
|
|
6321
6418
|
}
|
|
6322
6419
|
};
|
|
6323
6420
|
const prev = chatLocks.get(chatId) || Promise.resolve();
|
|
@@ -6354,7 +6451,7 @@ Describe or analyze the image if you can, or acknowledge it.`
|
|
|
6354
6451
|
for (const chunk of chunks) await ctx.api.sendMessage(chatId, chunk);
|
|
6355
6452
|
}
|
|
6356
6453
|
} catch (err) {
|
|
6357
|
-
await ctx.api.sendMessage(chatId,
|
|
6454
|
+
await ctx.api.sendMessage(chatId, `\u26A0\uFE0F Error: ${(err instanceof Error ? err.message : String(err)).slice(0, 200)}`);
|
|
6358
6455
|
}
|
|
6359
6456
|
};
|
|
6360
6457
|
const prev = chatLocks.get(chatId) || Promise.resolve();
|
|
@@ -6406,7 +6503,7 @@ You can read this file with the read_file tool.`
|
|
|
6406
6503
|
for (const chunk of chunks) await ctx.api.sendMessage(chatId, chunk);
|
|
6407
6504
|
}
|
|
6408
6505
|
} catch (err) {
|
|
6409
|
-
await ctx.api.sendMessage(chatId,
|
|
6506
|
+
await ctx.api.sendMessage(chatId, `\u26A0\uFE0F Error: ${(err instanceof Error ? err.message : String(err)).slice(0, 200)}`);
|
|
6410
6507
|
}
|
|
6411
6508
|
};
|
|
6412
6509
|
const prev = chatLocks.get(chatId) || Promise.resolve();
|
|
@@ -6441,45 +6538,94 @@ You can read this file with the read_file tool.`
|
|
|
6441
6538
|
case "help":
|
|
6442
6539
|
case "start":
|
|
6443
6540
|
return [
|
|
6444
|
-
"*Clank Commands*",
|
|
6541
|
+
"\u{1F527} *Clank Commands*",
|
|
6445
6542
|
"",
|
|
6446
|
-
"
|
|
6447
|
-
"/status \u2014 Agent and model info",
|
|
6448
|
-
"/agents \u2014 List available agents",
|
|
6449
|
-
"/agent <name> \u2014 Switch to a different agent",
|
|
6450
|
-
"/sessions \u2014 List recent sessions",
|
|
6543
|
+
"\u{1F4AC} *Chat*",
|
|
6451
6544
|
"/new \u2014 Start a new session",
|
|
6452
|
-
"/reset \u2014 Clear current session",
|
|
6545
|
+
"/reset \u2014 Clear current session history",
|
|
6546
|
+
"",
|
|
6547
|
+
"\u{1F4CA} *Info*",
|
|
6548
|
+
"/status \u2014 Agent, model, and session info",
|
|
6549
|
+
"/agents \u2014 List available agents",
|
|
6453
6550
|
"/model \u2014 Show current model",
|
|
6454
6551
|
"/tasks \u2014 Show background tasks",
|
|
6455
|
-
"/
|
|
6552
|
+
"/kill <id> \u2014 Kill a background task",
|
|
6553
|
+
"/killall \u2014 Kill all running tasks",
|
|
6554
|
+
"/version \u2014 Show Clank version",
|
|
6555
|
+
"",
|
|
6556
|
+
"\u2699\uFE0F *Settings*",
|
|
6557
|
+
"/agent <name> \u2014 Switch to a different agent",
|
|
6558
|
+
"/think \u2014 Toggle thinking display",
|
|
6559
|
+
"",
|
|
6560
|
+
"_Send any message to chat with the agent._"
|
|
6456
6561
|
].join("\n");
|
|
6457
6562
|
case "status": {
|
|
6458
6563
|
const cfg = this.config;
|
|
6459
6564
|
const model = cfg?.agents?.defaults?.model?.primary || "unknown";
|
|
6460
|
-
const
|
|
6565
|
+
const agentCount = cfg?.agents?.list?.length || 0;
|
|
6566
|
+
const tasks = this.gateway?.getTaskRegistry()?.list() || [];
|
|
6567
|
+
const runningTasks = tasks.filter((t) => t.status === "running").length;
|
|
6568
|
+
const uptime = Math.round((Date.now() - this.startedAt) / 6e4);
|
|
6569
|
+
const thinking = thinkingEnabled.get(chatId) ? "on" : "off";
|
|
6461
6570
|
return [
|
|
6462
|
-
"*Status*",
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6571
|
+
"\u{1F4CA} *Status*",
|
|
6572
|
+
"",
|
|
6573
|
+
`*Model:* \`${model}\``,
|
|
6574
|
+
`*Agents:* ${agentCount || 1} configured`,
|
|
6575
|
+
`*Tasks:* ${runningTasks} running / ${tasks.length} total`,
|
|
6576
|
+
`*Thinking:* ${thinking}`,
|
|
6577
|
+
`*Chat:* ${isGroup ? "group" : "DM"} (\`${chatId}\`)`,
|
|
6578
|
+
`*Uptime:* ${uptime} min`
|
|
6466
6579
|
].join("\n");
|
|
6467
6580
|
}
|
|
6468
6581
|
case "agents": {
|
|
6469
6582
|
const list = this.config?.agents?.list || [];
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6583
|
+
const defaultModel = this.config?.agents?.defaults?.model?.primary || "unknown";
|
|
6584
|
+
if (list.length === 0) {
|
|
6585
|
+
return `\u{1F4CB} *Agents*
|
|
6586
|
+
|
|
6587
|
+
\u2022 *default* \u2014 \`${defaultModel}\`
|
|
6588
|
+
|
|
6589
|
+
_No custom agents. Configure in config.json5._`;
|
|
6590
|
+
}
|
|
6591
|
+
const lines = list.map(
|
|
6592
|
+
(a) => `\u2022 *${a.name || a.id}* \u2014 \`${a.model?.primary || defaultModel}\``
|
|
6593
|
+
);
|
|
6594
|
+
return `\u{1F4CB} *Agents*
|
|
6595
|
+
|
|
6596
|
+
\u2022 *default* \u2014 \`${defaultModel}\`
|
|
6597
|
+
${lines.join("\n")}
|
|
6598
|
+
|
|
6599
|
+
_Switch with /agent <name>_`;
|
|
6600
|
+
}
|
|
6601
|
+
case "agent": {
|
|
6602
|
+
if (!args[0]) return "Usage: /agent <name>\n\nSee /agents for available agents.";
|
|
6603
|
+
const targetId = args[0].toLowerCase();
|
|
6604
|
+
const list = this.config?.agents?.list || [];
|
|
6605
|
+
const found = list.find((a) => a.id.toLowerCase() === targetId || (a.name || "").toLowerCase() === targetId);
|
|
6606
|
+
if (!found && targetId !== "default") {
|
|
6607
|
+
return `Agent "${args[0]}" not found. See /agents for available agents.`;
|
|
6608
|
+
}
|
|
6609
|
+
if (this.gateway) {
|
|
6610
|
+
await this.gateway.resetSession({
|
|
6611
|
+
channel: "telegram",
|
|
6612
|
+
peerId: chatId,
|
|
6613
|
+
peerKind: isGroup ? "group" : "dm"
|
|
6614
|
+
});
|
|
6478
6615
|
}
|
|
6479
|
-
|
|
6616
|
+
const name = found ? found.name || found.id : "default";
|
|
6617
|
+
return `Switched to agent *${name}*. Session reset \u2014 send a message to begin.`;
|
|
6618
|
+
}
|
|
6480
6619
|
case "sessions": {
|
|
6481
|
-
if (!this.gateway) return "Gateway not connected";
|
|
6482
|
-
return
|
|
6620
|
+
if (!this.gateway) return "Gateway not connected.";
|
|
6621
|
+
return [
|
|
6622
|
+
"\u{1F4C1} *Sessions*",
|
|
6623
|
+
"",
|
|
6624
|
+
"/new \u2014 Start a fresh session",
|
|
6625
|
+
"/reset \u2014 Clear current session history",
|
|
6626
|
+
"",
|
|
6627
|
+
`Current: \`${isGroup ? "group" : "dm"}:telegram:${chatId}\``
|
|
6628
|
+
].join("\n");
|
|
6483
6629
|
}
|
|
6484
6630
|
case "new":
|
|
6485
6631
|
case "reset":
|
|
@@ -6490,23 +6636,78 @@ You can read this file with the read_file tool.`
|
|
|
6490
6636
|
peerKind: isGroup ? "group" : "dm"
|
|
6491
6637
|
});
|
|
6492
6638
|
}
|
|
6493
|
-
return command === "new" ? "New session started. Send a message to begin." : "Session
|
|
6639
|
+
return command === "new" ? "\u2728 New session started. Send a message to begin." : "\u{1F5D1} Session cleared. History erased.";
|
|
6494
6640
|
case "model": {
|
|
6495
6641
|
const model = this.config?.agents?.defaults?.model?.primary || "unknown";
|
|
6496
|
-
|
|
6642
|
+
const fallbacks = this.config?.agents?.defaults?.model?.fallbacks || [];
|
|
6643
|
+
const lines = [`\u{1F916} *Current Model*
|
|
6644
|
+
|
|
6645
|
+
Primary: \`${model}\``];
|
|
6646
|
+
if (fallbacks.length > 0) {
|
|
6647
|
+
lines.push(`Fallbacks: ${fallbacks.map((f) => `\`${f}\``).join(", ")}`);
|
|
6648
|
+
}
|
|
6649
|
+
return lines.join("\n");
|
|
6497
6650
|
}
|
|
6498
6651
|
case "tasks": {
|
|
6499
6652
|
const tasks = this.gateway?.getTaskRegistry()?.list() || [];
|
|
6500
|
-
if (tasks.length === 0) return "No background tasks.";
|
|
6501
|
-
|
|
6653
|
+
if (tasks.length === 0) return "\u{1F4CB} No background tasks.";
|
|
6654
|
+
const lines = tasks.map((t) => {
|
|
6502
6655
|
const elapsed = Math.round(((t.completedAt || Date.now()) - t.startedAt) / 1e3);
|
|
6656
|
+
const status = t.status === "running" ? "\u23F3" : t.status === "completed" ? "\u2705" : t.status === "failed" ? "\u274C" : "\u23F1";
|
|
6503
6657
|
const depth = t.spawnDepth > 0 ? ` [depth ${t.spawnDepth}]` : "";
|
|
6504
6658
|
const kids = t.children.length > 0 ? ` (${t.children.length} children)` : "";
|
|
6505
|
-
|
|
6506
|
-
|
|
6659
|
+
const shortId = t.id.slice(0, 8);
|
|
6660
|
+
return `${status} \`${shortId}\` *${t.label.slice(0, 35)}* (${t.agentId})${depth}${kids} \u2014 ${elapsed}s`;
|
|
6661
|
+
});
|
|
6662
|
+
return `\u{1F4CB} *Background Tasks*
|
|
6663
|
+
|
|
6664
|
+
${lines.join("\n")}
|
|
6665
|
+
|
|
6666
|
+
_Kill with /kill <id> or /killall_`;
|
|
6667
|
+
}
|
|
6668
|
+
case "kill": {
|
|
6669
|
+
if (!this.gateway) return "Gateway not connected.";
|
|
6670
|
+
if (!args[0]) return "Usage: /kill <task-id>\n\nSee /tasks for task IDs.";
|
|
6671
|
+
const registry = this.gateway.getTaskRegistry();
|
|
6672
|
+
const shortId = args[0];
|
|
6673
|
+
const allTasks = registry.list();
|
|
6674
|
+
const match = allTasks.find((t) => t.id.startsWith(shortId) && t.status === "running");
|
|
6675
|
+
if (!match) return `No running task matching \`${shortId}\`. See /tasks.`;
|
|
6676
|
+
const subEngine = this.gateway.engines?.get(`task:${match.id}`);
|
|
6677
|
+
if (subEngine) {
|
|
6678
|
+
subEngine.cancel();
|
|
6679
|
+
subEngine.destroy();
|
|
6680
|
+
this.gateway.engines?.delete(`task:${match.id}`);
|
|
6681
|
+
}
|
|
6682
|
+
registry.cancel(match.id);
|
|
6683
|
+
const cascaded = registry.cascadeCancel(`task:${match.id}`);
|
|
6684
|
+
const cascade = cascaded > 0 ? ` + ${cascaded} child task(s)` : "";
|
|
6685
|
+
return `\u{1F5D1} Killed task \`${match.id.slice(0, 8)}\` \u2014 *${match.label.slice(0, 40)}*${cascade}`;
|
|
6686
|
+
}
|
|
6687
|
+
case "killall": {
|
|
6688
|
+
if (!this.gateway) return "Gateway not connected.";
|
|
6689
|
+
const registry = this.gateway.getTaskRegistry();
|
|
6690
|
+
const running = registry.list({ status: "running" });
|
|
6691
|
+
if (running.length === 0) return "No running tasks to kill.";
|
|
6692
|
+
for (const t of running) {
|
|
6693
|
+
const subEngine = this.gateway.engines?.get(`task:${t.id}`);
|
|
6694
|
+
if (subEngine) {
|
|
6695
|
+
subEngine.cancel();
|
|
6696
|
+
subEngine.destroy();
|
|
6697
|
+
this.gateway.engines?.delete(`task:${t.id}`);
|
|
6698
|
+
}
|
|
6699
|
+
registry.cancel(t.id);
|
|
6700
|
+
}
|
|
6701
|
+
return `\u{1F5D1} Killed *${running.length}* running task(s).`;
|
|
6702
|
+
}
|
|
6703
|
+
case "think": {
|
|
6704
|
+
const current = thinkingEnabled.get(chatId) ?? false;
|
|
6705
|
+
thinkingEnabled.set(chatId, !current);
|
|
6706
|
+
return !current ? "\u{1F4AD} Thinking display *on* \u2014 you'll see the model's reasoning above responses." : "\u{1F4AD} Thinking display *off* \u2014 only the final response will be shown.";
|
|
6707
|
+
}
|
|
6708
|
+
case "version": {
|
|
6709
|
+
return `\u{1F527} *Clank* v1.7.1`;
|
|
6507
6710
|
}
|
|
6508
|
-
case "think":
|
|
6509
|
-
return "Thinking display toggled. (Note: thinking visibility is per-client in the TUI/Web UI)";
|
|
6510
6711
|
default:
|
|
6511
6712
|
return null;
|
|
6512
6713
|
}
|
|
@@ -7006,6 +7207,11 @@ var init_server = __esm({
|
|
|
7006
7207
|
engine.on("token", fn);
|
|
7007
7208
|
listeners.push(["token", fn]);
|
|
7008
7209
|
}
|
|
7210
|
+
if (callbacks.onThinking) {
|
|
7211
|
+
const fn = (data) => callbacks.onThinking(data.content);
|
|
7212
|
+
engine.on("thinking", fn);
|
|
7213
|
+
listeners.push(["thinking", fn]);
|
|
7214
|
+
}
|
|
7009
7215
|
if (callbacks.onToolStart) {
|
|
7010
7216
|
const fn = (data) => callbacks.onToolStart(data.name);
|
|
7011
7217
|
engine.on("tool-start", fn);
|
|
@@ -7079,7 +7285,7 @@ var init_server = __esm({
|
|
|
7079
7285
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
7080
7286
|
res.end(JSON.stringify({
|
|
7081
7287
|
status: "ok",
|
|
7082
|
-
version: "1.7.
|
|
7288
|
+
version: "1.7.1",
|
|
7083
7289
|
uptime: process.uptime(),
|
|
7084
7290
|
clients: this.clients.size,
|
|
7085
7291
|
agents: this.engines.size
|
|
@@ -7191,7 +7397,7 @@ var init_server = __esm({
|
|
|
7191
7397
|
const hello = {
|
|
7192
7398
|
type: "hello",
|
|
7193
7399
|
protocol: PROTOCOL_VERSION,
|
|
7194
|
-
version: "1.7.
|
|
7400
|
+
version: "1.7.1",
|
|
7195
7401
|
agents: this.config.agents.list.map((a) => ({
|
|
7196
7402
|
id: a.id,
|
|
7197
7403
|
name: a.name || a.id,
|
|
@@ -8805,7 +9011,7 @@ async function runTui(opts) {
|
|
|
8805
9011
|
ws.on("open", () => {
|
|
8806
9012
|
ws.send(JSON.stringify({
|
|
8807
9013
|
type: "connect",
|
|
8808
|
-
params: { auth: { token }, mode: "tui", version: "1.7.
|
|
9014
|
+
params: { auth: { token }, mode: "tui", version: "1.7.1" }
|
|
8809
9015
|
}));
|
|
8810
9016
|
});
|
|
8811
9017
|
ws.on("message", (data) => {
|
|
@@ -9234,7 +9440,7 @@ import { fileURLToPath as fileURLToPath5 } from "url";
|
|
|
9234
9440
|
import { dirname as dirname5, join as join20 } from "path";
|
|
9235
9441
|
var __filename3 = fileURLToPath5(import.meta.url);
|
|
9236
9442
|
var __dirname3 = dirname5(__filename3);
|
|
9237
|
-
var version = "1.7.
|
|
9443
|
+
var version = "1.7.1";
|
|
9238
9444
|
try {
|
|
9239
9445
|
const pkg = JSON.parse(readFileSync(join20(__dirname3, "..", "package.json"), "utf-8"));
|
|
9240
9446
|
version = pkg.version;
|