@tractorscorch/clank 1.5.0 → 1.5.2
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 +18 -0
- package/README.md +2 -2
- package/dist/index.js +21 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [1.5.2] — 2026-03-23
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- **Thinking models (Qwen3.5) exhaust tokens on reasoning** — the model generates `<think>` reasoning tokens that eat the entire context window, leaving nothing for actual content. Added default `max_tokens: 4096` for local models and `reasoning_effort: "low"` to reduce thinking overhead
|
|
13
|
+
- **Telegram shows nothing during model thinking** — added periodic "typing" indicator every 4 seconds so the bot doesn't appear dead while the model processes internally
|
|
14
|
+
- **Root cause found via direct API testing** — Qwen3.5-35B returns empty `content` with all output in `reasoning_content`; without a max_tokens cap, the model spends all its budget on thinking
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## [1.5.1] — 2026-03-23
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **Local models timing out on tool calls** — removed per-chunk read timeout that was killing legitimate slow processing; a 35B quantized model can take minutes for prefill on large contexts, that's normal not a hang
|
|
22
|
+
- **Local model timeout increased to 5 minutes** — was 120s (too short for large quantized models doing prefill on big contexts with tool results)
|
|
23
|
+
- **Memory budget reduced for local models** — memory injection now uses 1.5K chars (was 4K) to avoid eating the limited context window of local models (8K-32K vs 128K+ for cloud)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
9
27
|
## [1.5.0] — 2026-03-23
|
|
10
28
|
|
|
11
29
|
### Fixed
|
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.5.
|
|
12
|
+
<a href="https://github.com/ItsTrag1c/Clank/releases/latest"><img src="https://img.shields.io/badge/version-1.5.2-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.5.
|
|
78
|
+
| **macOS** (Apple Silicon) | [Clank_1.5.2_macos](https://github.com/ItsTrag1c/Clank/releases/latest/download/Clank_1.5.2_macos) |
|
|
79
79
|
|
|
80
80
|
## Features
|
|
81
81
|
|
package/dist/index.js
CHANGED
|
@@ -561,7 +561,7 @@ var init_ollama = __esm({
|
|
|
561
561
|
if (this.maxResponseTokens) {
|
|
562
562
|
body.max_tokens = this.maxResponseTokens;
|
|
563
563
|
}
|
|
564
|
-
const timeoutSignal = AbortSignal.timeout(
|
|
564
|
+
const timeoutSignal = AbortSignal.timeout(3e5);
|
|
565
565
|
const effectiveSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
566
566
|
const res = await fetch(`${this.baseUrl}/v1/chat/completions`, {
|
|
567
567
|
method: "POST",
|
|
@@ -580,14 +580,9 @@ var init_ollama = __esm({
|
|
|
580
580
|
const decoder = new TextDecoder();
|
|
581
581
|
let buffer = "";
|
|
582
582
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
583
|
-
const CHUNK_TIMEOUT = 6e4;
|
|
584
583
|
try {
|
|
585
584
|
while (true) {
|
|
586
|
-
const
|
|
587
|
-
const timeoutPromise = new Promise(
|
|
588
|
-
(_, reject) => setTimeout(() => reject(new Error("Model stopped responding (no data for 60s)")), CHUNK_TIMEOUT)
|
|
589
|
-
);
|
|
590
|
-
const { done, value } = await Promise.race([readPromise, timeoutPromise]);
|
|
585
|
+
const { done, value } = await reader.read();
|
|
591
586
|
if (done) break;
|
|
592
587
|
buffer += decoder.decode(value, { stream: true });
|
|
593
588
|
const lines = buffer.split("\n");
|
|
@@ -2992,11 +2987,16 @@ var init_openai = __esm({
|
|
|
2992
2987
|
stream: true,
|
|
2993
2988
|
stream_options: { include_usage: true }
|
|
2994
2989
|
};
|
|
2990
|
+
if (this.isLocal) {
|
|
2991
|
+
body.reasoning_effort = "low";
|
|
2992
|
+
}
|
|
2995
2993
|
if (tools.length > 0) {
|
|
2996
2994
|
body.tools = this.formatTools(tools);
|
|
2997
2995
|
}
|
|
2998
2996
|
if (this.maxResponseTokens) {
|
|
2999
2997
|
body.max_tokens = this.maxResponseTokens;
|
|
2998
|
+
} else if (this.isLocal) {
|
|
2999
|
+
body.max_tokens = 4096;
|
|
3000
3000
|
}
|
|
3001
3001
|
const headers = {
|
|
3002
3002
|
"Content-Type": "application/json"
|
|
@@ -3004,7 +3004,7 @@ var init_openai = __esm({
|
|
|
3004
3004
|
if (this.apiKey) {
|
|
3005
3005
|
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
3006
3006
|
}
|
|
3007
|
-
const timeoutMs = this.isLocal ?
|
|
3007
|
+
const timeoutMs = this.isLocal ? 3e5 : 9e4;
|
|
3008
3008
|
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
3009
3009
|
const effectiveSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
3010
3010
|
const res = await fetch(`${this.baseUrl}/v1/chat/completions`, {
|
|
@@ -3022,14 +3022,9 @@ var init_openai = __esm({
|
|
|
3022
3022
|
const decoder = new TextDecoder();
|
|
3023
3023
|
let buffer = "";
|
|
3024
3024
|
const toolCalls = /* @__PURE__ */ new Map();
|
|
3025
|
-
const CHUNK_TIMEOUT = 6e4;
|
|
3026
3025
|
try {
|
|
3027
3026
|
while (true) {
|
|
3028
|
-
const
|
|
3029
|
-
const timeoutPromise = new Promise(
|
|
3030
|
-
(_, reject) => setTimeout(() => reject(new Error("Model stopped responding (no data for 60s)")), CHUNK_TIMEOUT)
|
|
3031
|
-
);
|
|
3032
|
-
const { done, value } = await Promise.race([readPromise, timeoutPromise]);
|
|
3027
|
+
const { done, value } = await reader.read();
|
|
3033
3028
|
if (done) break;
|
|
3034
3029
|
buffer += decoder.decode(value, { stream: true });
|
|
3035
3030
|
const lines = buffer.split("\n");
|
|
@@ -5308,6 +5303,10 @@ var init_telegram = __esm({
|
|
|
5308
5303
|
try {
|
|
5309
5304
|
console.log(` Telegram: processing message from ${userId} in ${chatId}`);
|
|
5310
5305
|
await ctx.api.sendChatAction(chatId, "typing");
|
|
5306
|
+
const typingInterval2 = setInterval(() => {
|
|
5307
|
+
bot.api.sendChatAction(chatId, "typing").catch(() => {
|
|
5308
|
+
});
|
|
5309
|
+
}, 4e3);
|
|
5311
5310
|
let streamMsgId = null;
|
|
5312
5311
|
let sendingInitial = false;
|
|
5313
5312
|
let accumulated = "";
|
|
@@ -5376,8 +5375,10 @@ var init_telegram = __esm({
|
|
|
5376
5375
|
await ctx.api.sendMessage(chatId, chunk);
|
|
5377
5376
|
}
|
|
5378
5377
|
}
|
|
5378
|
+
clearInterval(typingInterval2);
|
|
5379
5379
|
console.log(` Telegram: response complete (${response?.length || 0} chars)`);
|
|
5380
5380
|
} catch (err) {
|
|
5381
|
+
clearInterval(typingInterval);
|
|
5381
5382
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
5382
5383
|
console.error(` Telegram: message handler error \u2014 ${errMsg}`);
|
|
5383
5384
|
await ctx.api.sendMessage(chatId, `Error: ${errMsg.slice(0, 200)}`).catch(() => {
|
|
@@ -6165,7 +6166,7 @@ var init_server = __esm({
|
|
|
6165
6166
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
6166
6167
|
res.end(JSON.stringify({
|
|
6167
6168
|
status: "ok",
|
|
6168
|
-
version: "1.5.
|
|
6169
|
+
version: "1.5.2",
|
|
6169
6170
|
uptime: process.uptime(),
|
|
6170
6171
|
clients: this.clients.size,
|
|
6171
6172
|
agents: this.engines.size
|
|
@@ -6277,7 +6278,7 @@ var init_server = __esm({
|
|
|
6277
6278
|
const hello = {
|
|
6278
6279
|
type: "hello",
|
|
6279
6280
|
protocol: PROTOCOL_VERSION,
|
|
6280
|
-
version: "1.5.
|
|
6281
|
+
version: "1.5.2",
|
|
6281
6282
|
agents: this.config.agents.list.map((a) => ({
|
|
6282
6283
|
id: a.id,
|
|
6283
6284
|
name: a.name || a.id,
|
|
@@ -6519,7 +6520,8 @@ var init_server = __esm({
|
|
|
6519
6520
|
compact,
|
|
6520
6521
|
thinking
|
|
6521
6522
|
});
|
|
6522
|
-
const
|
|
6523
|
+
const memoryBudget = resolved.isLocal ? 1500 : 4e3;
|
|
6524
|
+
const memoryBlock = await this.memoryManager.buildMemoryBlock("", identity.workspace, memoryBudget);
|
|
6523
6525
|
const fullPrompt = memoryBlock ? systemPrompt + "\n\n---\n\n" + memoryBlock : systemPrompt;
|
|
6524
6526
|
engine = new AgentEngine({
|
|
6525
6527
|
identity,
|
|
@@ -7671,7 +7673,7 @@ async function runTui(opts) {
|
|
|
7671
7673
|
ws.on("open", () => {
|
|
7672
7674
|
ws.send(JSON.stringify({
|
|
7673
7675
|
type: "connect",
|
|
7674
|
-
params: { auth: { token }, mode: "tui", version: "1.5.
|
|
7676
|
+
params: { auth: { token }, mode: "tui", version: "1.5.2" }
|
|
7675
7677
|
}));
|
|
7676
7678
|
});
|
|
7677
7679
|
ws.on("message", (data) => {
|
|
@@ -8100,7 +8102,7 @@ import { fileURLToPath as fileURLToPath5 } from "url";
|
|
|
8100
8102
|
import { dirname as dirname5, join as join19 } from "path";
|
|
8101
8103
|
var __filename3 = fileURLToPath5(import.meta.url);
|
|
8102
8104
|
var __dirname3 = dirname5(__filename3);
|
|
8103
|
-
var version = "1.5.
|
|
8105
|
+
var version = "1.5.2";
|
|
8104
8106
|
try {
|
|
8105
8107
|
const pkg = JSON.parse(readFileSync(join19(__dirname3, "..", "package.json"), "utf-8"));
|
|
8106
8108
|
version = pkg.version;
|